How to Concat in XSLT

H

Hercules Dev.

Hi all,

I'm new in xslt and xpath, so my question might be simple but i'm
learning.

I have an XML document and need to transform it into another XML, I use
xslt and it works, but there is a case that i don't know how to solve,
I need to concat a string from multiple childs into a standard way, the
following is an example of the source and the target XML.

Source:

<Root>
<ParentElementX>
<ChildElement>
<Value>1</Value>
<Value>2</Value>
<Value>3</Value>
<ChildElement>
<ChildElement>
<Value>4</Value>
<Value>5</Value>
<Value>6</Value>
<ChildElement>

</ParentElementX>
<ParentElementX>
<ChildElement>
<Value>1</Value>
<Value>2</Value>
<Value>3</Value>
<ChildElement>
</ParentElementX>
</Root>

Destination:

<Root>
<ParentElementX>
<ChildElement>1/2/3</ChildElement>
<ChildElement>4/56<ChildElement>
</ParentElementX>
<ParentElementX>
<ChildElement>1/2/3<ChildElement>
</ParentElementX>
</Root>

Please tell me what is the xslt code I need to write to transform this?

Thanks,
Zaid H. Safadi
 
P

p.lepin

Source:

<Root>
<ParentElementX>
<ChildElement>
<Value>1</Value>
<Value>2</Value>
<Value>3</Value>
<ChildElement>
<ChildElement>
<Value>4</Value>
<Value>5</Value>
<Value>6</Value>
<ChildElement>

</ParentElementX>
<ParentElementX>
<ChildElement>
<Value>1</Value>
<Value>2</Value>
<Value>3</Value>
<ChildElement>
</ParentElementX>
</Root>

Destination:

<Root>
<ParentElementX>
<ChildElement>1/2/3</ChildElement>
<ChildElement>4/56<ChildElement>
</ParentElementX>
<ParentElementX>
<ChildElement>1/2/3<ChildElement>
</ParentElementX>
</Root>

Please tell me what is the xslt code I need to write to
transform this?

You can't, because the source you provided is not
well-formed XML. So unless you write your own parser that
would transform this proprietary document into XML DOM that
your XSLT would grok, it's simply impossible.

Even if you write your own parser, I'm not really certain
how did you get to '4/56' as content for the second
ChildElement element in your resulting document.

Now, if you were using well-formed XML, I suppose you could
do something like this:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template
match="Value[not(preceding-sibling::Value)]">
<xsl:apply-templates select="text()"/>
<xsl:apply-templates
select="following-sibling::*[self::Value]"
mode="append"/>
</xsl:template>
<xsl:template match="Value[preceding-sibling::Value]"/>
<xsl:template match="*" mode="append">
<xsl:text>/</xsl:text>
<xsl:apply-templates select="text()"/>
</xsl:template>
</xsl:stylesheet>

(Hint: posting sloppy examples is not a very good idea if
you're asking for help.)
 
G

George Bina

As XSLT 2.0 has just been released a couple of days ago, here it an
XSLT 2.0 solution. To concatenate all the values separated with slashes
it just needs
<xsl:value-of select="Value" separator="/"/>


<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ChildElement">
<xsl:copy>
<xsl:value-of select="Value" separator="/"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Regards,
George
---------------------------------------------------------------------
George Cristian Bina
<oXygen/> XML Editor, Schema Editor and XSLT Editor/Debugger
http://www.oxygenxml.com

Source:




Please tell me what is the xslt code I need to write to
transform this?You can't, because the source you provided is not
well-formed XML. So unless you write your own parser that
would transform this proprietary document into XML DOM that
your XSLT would grok, it's simply impossible.

Even if you write your own parser, I'm not really certain
how did you get to '4/56' as content for the second
ChildElement element in your resulting document.

Now, if you were using well-formed XML, I suppose you could
do something like this:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template
match="Value[not(preceding-sibling::Value)]">
<xsl:apply-templates select="text()"/>
<xsl:apply-templates
select="following-sibling::*[self::Value]"
mode="append"/>
</xsl:template>
<xsl:template match="Value[preceding-sibling::Value]"/>
<xsl:template match="*" mode="append">
<xsl:text>/</xsl:text>
<xsl:apply-templates select="text()"/>
</xsl:template>
</xsl:stylesheet>

(Hint: posting sloppy examples is not a very good idea if
you're asking for help.)
 
H

Hercules Dev.

Pavel Lepin,


Thanks for your reply, this is exactly what I wanted and sorry for my
bad source example I usually don't post such bad examples.

Your code worked well with my example but I didn't know how to
integrate it with my already existent XLST code, so I had to perform
two transformations for both using both XSLT, so to solve this I need
to understand your XSLT, is there any good reference you know that can
help me to understand this XSLT code?

Also if you can explain even if a little of it then it will be great.

Thanks anyway,
Zaid H. Safadi


Source:




Please tell me what is the xslt code I need to write to
transform this?You can't, because the source you provided is not
well-formed XML. So unless you write your own parser that
would transform this proprietary document into XML DOM that
your XSLT would grok, it's simply impossible.

Even if you write your own parser, I'm not really certain
how did you get to '4/56' as content for the second
ChildElement element in your resulting document.

Now, if you were using well-formed XML, I suppose you could
do something like this:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template
match="Value[not(preceding-sibling::Value)]">
<xsl:apply-templates select="text()"/>
<xsl:apply-templates
select="following-sibling::*[self::Value]"
mode="append"/>
</xsl:template>
<xsl:template match="Value[preceding-sibling::Value]"/>
<xsl:template match="*" mode="append">
<xsl:text>/</xsl:text>
<xsl:apply-templates select="text()"/>
</xsl:template>
</xsl:stylesheet>

(Hint: posting sloppy examples is not a very good idea if
you're asking for help.)
 
H

Hercules Dev.

George,

Thanks for your reply, I'm using the .NET Framework 1.1 engine for XSLT
transformation so i don't think it support XSLT 2.0, I'm already
planning to convert my work to .NET 2.0 so maybe i can make use of the
new features of XSLT 2.0

Thanks,
Zaid H. Safadi

As XSLT 2.0 has just been released a couple of days ago, here it an
XSLT 2.0 solution. To concatenate all the values separated with slashes
it just needs
<xsl:value-of select="Value" separator="/"/>

<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ChildElement">
<xsl:copy>
<xsl:value-of select="Value" separator="/"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Regards,
George
---------------------------------------------------------------------
George Cristian Bina
<oXygen/> XML Editor, Schema Editor and XSLT Editor/Debuggerhttp://www.oxygenxml.com

On Jan 26, 12:53 pm, "Hercules Dev." <[email protected]>
wrote:
well-formed XML. So unless you write your own parser that
would transform this proprietary document into XML DOM that
your XSLT would grok, it's simply impossible.
Even if you write your own parser, I'm not really certain
how did you get to '4/56' as content for the second
ChildElement element in your resulting document.
Now, if you were using well-formed XML, I suppose you could
do something like this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template
match="Value[not(preceding-sibling::Value)]">
<xsl:apply-templates select="text()"/>
<xsl:apply-templates
select="following-sibling::*[self::Value]"
mode="append"/>
</xsl:template>
<xsl:template match="Value[preceding-sibling::Value]"/>
<xsl:template match="*" mode="append">
<xsl:text>/</xsl:text>
<xsl:apply-templates select="text()"/>
</xsl:template>
</xsl:stylesheet>
(Hint: posting sloppy examples is not a very good idea if
you're asking for help.)
 
M

Martin Honnen

Hercules said:
I'm using the .NET Framework 1.1 engine for XSLT
transformation so i don't think it support XSLT 2.0, I'm already
planning to convert my work to .NET 2.0 so maybe i can make use of the
new features of XSLT 2.0

Both System.Xml.Xsl.XslTransform in .NET 1.x and
System.Xml.Xsl.XslCompiledTransform in .NET 2.0 support XSLT 1.0.
If you want to use XSLT 2.0 with .NET then you need Saxon from
<http://www.saxonica.com/>.
 
P

Peter Flynn

Hercules said:
Hi all,

I'm new in xslt and xpath, so my question might be simple but i'm
learning.

I have an XML document and need to transform it into another XML, I use
xslt and it works, but there is a case that i don't know how to solve,
I need to concat a string from multiple childs into a standard way, the
following is an example of the source and the target XML.

Source:

<Root>
<ParentElementX>
<ChildElement>
<Value>1</Value>
<Value>2</Value>
<Value>3</Value>
<ChildElement>
<ChildElement>
<Value>4</Value>
<Value>5</Value>
<Value>6</Value>
<ChildElement>

</ParentElementX>
<ParentElementX>
<ChildElement>
<Value>1</Value>
<Value>2</Value>
<Value>3</Value>
<ChildElement>
</ParentElementX>
</Root>

That's not XML. Assuming you just mistyped it and that the ChildElements
do actually have start-tags and end-tags, and that you also missed a
slash in the "4/5/6" output, then there are two ways:

1. Concatenation using a recursive named template:

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:eek:utput method="xml" indent="yes"/>

<xsl:template match="Root">
<Root>
<xsl:apply-templates/>
</Root>
</xsl:template>

<xsl:template match="ParentElementX">
<ParentElementX>
<xsl:apply-templates/>
</ParentElementX>
</xsl:template>

<xsl:template match="ChildElement">
<ChildElement>
<xsl:call-template name="join-with-slashes">
<xsl:with-param name="children" select="Value"/>
</xsl:call-template>
</ChildElement>
</xsl:template>

<xsl:template name="join-with-slashes">
<xsl:param name="children"/>
<xsl:param name="counter">
<xsl:text>1</xsl:text>
</xsl:param>
<xsl:choose>
<xsl:when test="$counter>=count($children)">
<xsl:text>/</xsl:text>
<xsl:value-of select="$children[$counter]"/>
</xsl:when>
<xsl:eek:therwise>
<xsl:if test="$counter>1">
<xsl:text>/</xsl:text>
</xsl:if>
<xsl:value-of select="$children[$counter]"/>
<xsl:call-template name="join-with-slashes">
<xsl:with-param name="children" select="$children"/>
<xsl:with-param name="counter" select="$counter+1"/>
</xsl:call-template>
</xsl:eek:therwise>
</xsl:choose>
</xsl:template>

</xsl:stylesheet>

2. Concatenation using normal templates (much faster):

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:eek:utput method="xml" indent="yes"/>
<xsl:strip-space elements="ChildElement"/>

<xsl:template match="Root">
<Root>
<xsl:apply-templates/>
</Root>
</xsl:template>

<xsl:template match="ParentElementX">
<ParentElementX>
<xsl:apply-templates/>
</ParentElementX>
</xsl:template>

<xsl:template match="ChildElement">
<ChildElement>
<xsl:apply-templates/>
</ChildElement>
</xsl:template>

<xsl:template match="Value">
<xsl:value-of select="."/>
<xsl:if test="position()!=last()">
<xsl:text>/</xsl:text>
</xsl:if>
</xsl:template>

</xsl:stylesheet>

///Peter
 
H

Hercules Dev.

Thank you so much guys,

Yes my source example was bad and I meant to add en end tag for each
ChildElement and I forgot to include the slash to output 4/5/6.

But I read a book about XML, Xpath, Schema and XSLT "Addison Wesley -
Essential Xml Reference - Xpath, Xslt, Soap.pdf" and it's great, I am
able to understand your examples now and it worked with me and solved
my problems, so really thanks for all contributors.

Thanks,
Zaid H. Safadi



Hercules said:
I'm new in xslt and xpath, so my question might be simple but i'm
learning.
I have an XML document and need to transform it into another XML, I use
xslt and it works, but there is a case that i don't know how to solve,
I need to concat a string from multiple childs into a standard way, the
following is an example of the source and the target XML.


</ParentElementX>
<ParentElementX>
<ChildElement>
<Value>1</Value>
<Value>2</Value>
<Value>3</Value>
<ChildElement>
</ParentElementX>
</Root>That's not XML. Assuming you just mistyped it and that the ChildElements
do actually have start-tags and end-tags, and that you also missed a
slash in the "4/5/6" output, then there are two ways:

1. Concatenation using a recursive named template:

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:eek:utput method="xml" indent="yes"/>

<xsl:template match="Root">
<Root>
<xsl:apply-templates/>
</Root>
</xsl:template>

<xsl:template match="ParentElementX">
<ParentElementX>
<xsl:apply-templates/>
</ParentElementX>
</xsl:template>

<xsl:template match="ChildElement">
<ChildElement>
<xsl:call-template name="join-with-slashes">
<xsl:with-param name="children" select="Value"/>
</xsl:call-template>
</ChildElement>
</xsl:template>

<xsl:template name="join-with-slashes">
<xsl:param name="children"/>
<xsl:param name="counter">
<xsl:text>1</xsl:text>
</xsl:param>
<xsl:choose>
<xsl:when test="$counter>=count($children)">
<xsl:text>/</xsl:text>
<xsl:value-of select="$children[$counter]"/>
</xsl:when>
<xsl:eek:therwise>
<xsl:if test="$counter>1">
<xsl:text>/</xsl:text>
</xsl:if>
<xsl:value-of select="$children[$counter]"/>
<xsl:call-template name="join-with-slashes">
<xsl:with-param name="children" select="$children"/>
<xsl:with-param name="counter" select="$counter+1"/>
</xsl:call-template>
</xsl:eek:therwise>
</xsl:choose>
</xsl:template>

</xsl:stylesheet>

2. Concatenation using normal templates (much faster):

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:eek:utput method="xml" indent="yes"/>
<xsl:strip-space elements="ChildElement"/>

<xsl:template match="Root">
<Root>
<xsl:apply-templates/>
</Root>
</xsl:template>

<xsl:template match="ParentElementX">
<ParentElementX>
<xsl:apply-templates/>
</ParentElementX>
</xsl:template>

<xsl:template match="ChildElement">
<ChildElement>
<xsl:apply-templates/>
</ChildElement>
</xsl:template>

<xsl:template match="Value">
<xsl:value-of select="."/>
<xsl:if test="position()!=last()">
<xsl:text>/</xsl:text>
</xsl:if>
</xsl:template>

</xsl:stylesheet>

///Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,165
Latest member
JavierBrak
Top