Reputation: 283
How can i handle this, to concate similar XML Tags in XML File
I have XMl in following format::
<addressbook>
<address>
<first-name>Chester Hasbrouck</first-name>
<Descritpion>Hi</Descritpion>
<Descritpion>This is Chester </Descritpion>
<street>1234 Main Street</street>
<city>Sheboygan</city>
<state>WI</state>
<zip>48392</zip>
</address>
<address>
<first-name>Mary</first-name>
<Descritpion>Hi</Descritpion>
<Descritpion>This is Mary </Descritpion>
<Descritpion>Bye</Descritpion>
<street>283 First Avenue</street>
<city>Skunk Haven</city>
<state>MA</state>
<zip>02718</zip>
</address>
</addressbook>
I need this in following format somebody please suggest XSLT for that.
<addressbook>
<address>
<first-name>Chester Hasbrouck</first-name>
<Descritpion>Hi | This is Chester </Descritpion>
<street>1234 Main Street</street>
<city>Sheboygan</city>
<state>WI</state>
<zip>48392</zip>
</address>
<address>
<first-name>Mary</first-name>
<Descritpion>Hi | This is Mary | Bye</Descritpion>
<street>283 First Avenue</street>
<city>Skunk Haven</city>
<state>MA</state>
<zip>02718</zip>
</address>
</addressbook>
Upvotes: 2
Views: 145
Reputation: 3738
Here's one more way to do it.
One thing to note: this solution doesn't maintain the output document ordering (i.e., it doesn't create the new <Descritpion>
element directly under the <first-name>
element). If that's important to you, have a look at the other provided solutions.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- TEMPLATE #1 -->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<!-- TEMPLATE #2 -->
<xsl:template match="address">
<xsl:copy>
<xsl:apply-templates/>
<Descritpion>
<xsl:apply-templates select="Descritpion/text()"/>
</Descritpion>
</xsl:copy>
</xsl:template>
<!-- TEMPLATE #3 -->
<xsl:template match="Descritpion/text()">
<xsl:value-of select="." />
<xsl:if test="position() != last()"> | </xsl:if>
</xsl:template>
<!-- TEMPLATE #4 -->
<xsl:template match="Descritpion" />
</xsl:stylesheet>
When this is provided on the provided XML document:
<?xml version="1.0" encoding="UTF-8"?>
<addressbook>
<address>
<first-name>Chester Hasbrouck</first-name>
<Descritpion>Hi</Descritpion>
<Descritpion>This is Chester </Descritpion>
<street>1234 Main Street</street>
<city>Sheboygan</city>
<state>WI</state>
<zip>48392</zip>
</address>
<address>
<first-name>Mary</first-name>
<Descritpion>Hi</Descritpion>
<Descritpion>This is Mary </Descritpion>
<Descritpion>Bye</Descritpion>
<street>283 First Avenue</street>
<city>Skunk Haven</city>
<state>MA</state>
<zip>02718</zip>
</address>
</addressbook>
...the desired result is produced:
<?xml version="1.0" encoding="UTF-8"?><addressbook>
<address>
<first-name>Chester Hasbrouck</first-name>
<street>1234 Main Street</street>
<city>Sheboygan</city>
<state>WI</state>
<zip>48392</zip>
<Descritpion>Hi | This is Chester </Descritpion>
</address>
<address>
<first-name>Mary</first-name>
<street>283 First Avenue</street>
<city>Skunk Haven</city>
<state>MA</state>
<zip>02718</zip>
<Descritpion>Hi | This is Mary | Bye</Descritpion>
</address>
</addressbook>
Explanation:
Template #1: the "Identity Template" copies everything as-is.
Template #2: this template copies the each <address>
element as-is, creates a new <Descritpion>
element, and instructs the XSLT processor to fill that new element with the results from any template that matches Descritpion/text()
.
Template #3: this template processes the results from Template #2; in particular, it copies the text from all the old <Descritpion>
elements and, until it reaches the last one, places a |
in between the results.
Template #4: this template removes the original <Descritpion>
elements from the source document.
Upvotes: 0
Reputation: 243469
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Descritpion[not(preceding-sibling::*[1][self::Descritpion])]">
<Descritpion>
<xsl:value-of select="."/>
<xsl:apply-templates select="following-sibling::Descritpion/text()"/>
</Descritpion>
</xsl:template>
<xsl:template match="Descritpion/text()">
<xsl:value-of select="concat(' | ', .)"/>
</xsl:template>
<xsl:template match="Descritpion"/>
</xsl:stylesheet>
when applied on the provided XML document:
<addressbook>
<address>
<first-name>Chester Hasbrouck</first-name>
<Descritpion>Hi</Descritpion>
<Descritpion>This is Chester </Descritpion>
<street>1234 Main Street</street>
<city>Sheboygan</city>
<state>WI</state>
<zip>48392</zip>
</address>
<address>
<first-name>Mary</first-name>
<Descritpion>Hi</Descritpion>
<Descritpion>This is Mary </Descritpion>
<Descritpion>Bye</Descritpion>
<street>283 First Avenue</street>
<city>Skunk Haven</city>
<state>MA</state>
<zip>02718</zip>
</address>
</addressbook>
produces the wanted, correct result:
<addressbook>
<address>
<first-name>Chester Hasbrouck</first-name>
<Descritpion>Hi | This is Chester </Descritpion>
<street>1234 Main Street</street>
<city>Sheboygan</city>
<state>WI</state>
<zip>48392</zip>
</address>
<address>
<first-name>Mary</first-name>
<Descritpion>Hi | This is Mary | Bye</Descritpion>
<street>283 First Avenue</street>
<city>Skunk Haven</city>
<state>MA</state>
<zip>02718</zip>
</address>
</addressbook>
Explanation:
The identity rule copies every node "as-is".
Descritpion
elements are generally ignored / deleted by overriding the identity template with an empty-bodied template matching Descritpion
.
Only a Descritpion
element whose immediately preceding sibling element isn't a Descritpion
itself is treated specially by another overriding template. Here the string value of the element is copied and then templates are applied to all text-node-children of the following siblings Descritpion
elements.
A template matching a text-node-child of aDescritpion
element implements the wanted text concatenation.
Upvotes: 1
Reputation: 70608
To do this, you can build upon the identity transform, but add in extra rules to handle the elements you are interested in
I think you first need to match elements which don't have child elements in them
<xsl:template match="*[not(*)]" priority="1">
You would then add code to this to output the current text value, and then select the text value of all following siblings with the same name (which would be output with the separator before them)
<xsl:apply-templates
select="following-sibling::*[local-name() = local-name(current())]/text()"
mode="concat"/>
You would also need to add a template to ignore elements which have the same name as a preceding sibling so they do not get output twice
<xsl:template
match="*[not(*)][local-name() = local-name(preceding-sibling::*[1])]"
priority="2" />
Do note the use of the priority attribute here, so the XSLT processor matches the one with the higher priority first.
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="*[not(*)]" priority="1">
<xsl:copy>
<xsl:value-of select="normalize-space(.)"/>
<xsl:apply-templates select="following-sibling::*[local-name() = local-name(current())]/text()" mode="concat"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(*)][local-name() = local-name(preceding-sibling::*[1])]" priority="2"/>
<xsl:template match="text()" mode="concat">
<xsl:value-of select="concat(' | ', normalize-space(.))"/>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, the following is output
<addressbook>
<address>
<first-name>Chester Hasbrouck</first-name>
<Descritpion>Hi | This is Chester</Descritpion>
<street>1234 Main Street</street>
<city>Sheboygan</city>
<state>WI</state>
<zip>48392</zip>
</address>
<address>
<first-name>Mary</first-name>
<Descritpion>Hi | This is Mary | Bye</Descritpion>
<street>283 First Avenue</street>
<city>Skunk Haven</city>
<state>MA</state>
<zip>02718</zip>
</address>
</addressbook>
Upvotes: 0