some text

XML node grouping

Im working with PHP5, and I need to convert the XML in the following form:

<list> <item label="(1)">some text</item> <item label="(2)"> <anotherNode>some text</anotherNode <item label="a">some text</item> <item label="b">some text</item> </item> </list> 

In something like this:

 <list> <item label="(1)">some text</item> <item label="(2)"> <anotherNode>some text</anotherNode> <list> <!-- opening new wrapper node--> <item label="a">some text</item> <item label="b">some text</item> </list> <!-- closing new wrapper node--> </item> </list> 

As you can see above, I need to add a node wrapper to any item nodes that are no longer wrapped in a list of nodes.

What are the possible solutions to convert source xml to target xml?

UPDATED:

Note 1: Any single or group of nodes <item> should be wrapped <list> node, if it is not already completed.

Note 2: The content order must be saved.

Note 3: If there are <item> nodes before and after <anotherNode> . He should convert this:

 <list> <item label="(1)">some text</item> <item label="(2)"> <item label="a">some text</item> <item label="b">some text</item> <anotherNode>some text</anotherNode> <item label="c">some text</item> <item label="d">some text</item> </item> </list> 

in it:

 <list> <item label="(1)">some text</item> <item label="(2)"> <list> <!-- opening new wrapper node--> <item label="a">some text</item> <item label="b">some text</item> </list> <!-- closing new wrapper node--> <anotherNode>some text</anotherNode> <list> <!-- opening new wrapper node--> <item label="c">some text</item> <item label="d">some text</item> </list> <!-- closing new wrapper node--> </item> </list> 

Thanks,

+4
source share
3 answers

This conversion is :

 <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()|@*" name="identity"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="item/item[1]"> <list> <xsl:apply-templates mode="copy" select=".| following-sibling::item"/> </list> </xsl:template> <xsl:template match="item" mode="copy"> <xsl:call-template name="identity"/> </xsl:template> <xsl:template match="item/item[not(position()=1)]"/> </xsl:stylesheet> 

when applied to the provided XML document :

 <list> <item label="(1)">some text</item> <item label="(2)"> <anotherNode>some text</anotherNode> <item label="a">some text</item> <item label="b">some text</item> </item> </list> 

creates the desired, correct result :

 <list> <item label="(1)">some text</item> <item label="(2)"> <anotherNode>some text</anotherNode> <list> <item label="a">some text</item> <item label="b">some text</item> </list> </item> </list> 

Please note :

  • Using and overriding the Identity rule.

  • Suppression of certain elements.

  • Processing some elements using a different mode.

Update

OP added additional requirements:

"If there are item elements before and after anothernode , then each such group of item elements must be enclosed in a separate list "

 <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:key name="kfollnonitem" match="item" use="generate-id(preceding-sibling::*[not(self::item)][1])"/> <xsl:key name="kprecnonitem" match="item" use="generate-id(following-sibling::*[not(self::item)][1])"/> <xsl:template match="node()|@*" name="identity"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="*[not(self::list)]/item[1]"> <list> <xsl:apply-templates mode="copy" select="key('kprecnonitem', generate-id(following-sibling::*[not(self::item)][1]) )"/> </list> </xsl:template> <xsl:template match= "*[not(self::list) and item]/*[not(self::item)]"> <xsl:call-template name="identity"/> <list> <xsl:apply-templates mode="copy" select="key('kfollnonitem', generate-id())"/> </list> </xsl:template> <xsl:template match="item" mode="copy"> <xsl:call-template name="identity"/> </xsl:template> <xsl:template match="item/item[not(position()=1)]"/> </xsl:stylesheet> 

when this conversion is performed against an XML document :

 <list> <item label="(1)">some text</item> <item label="(2)"> <item label="a">some text</item> <item label="b">some text</item> <anotherNode>some text</anotherNode> <item label="c">some text</item> <item label="d">some text</item> </item> </list> 

required, the correct result is obtained :

 <list> <item label="(1)">some text</item> <item label="(2)"> <list> <item label="a">some text</item> <item label="b">some text</item> </list> <anotherNode>some text</anotherNode> <list> <item label="c">some text</item> <item label="d">some text</item> </list> </item> </list> 
+4
source

This style sheet:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="@*|node()" name="identity"> <xsl:copy> <xsl:apply-templates select="@*|node()[1]" /> </xsl:copy> <xsl:apply-templates select="following-sibling::node()[1]" /> </xsl:template> <xsl:template match="*[not(self::list)] /item[not(preceding-sibling::*[1][self::item])]"> <list> <xsl:call-template name="identity"/> </list> <xsl:apply-templates select="following-sibling::node() [not(self::item)][1]" /> </xsl:template> <xsl:template match="*[not(self::list)] /item[not(following-sibling::*[1][self::item])]"> <xsl:copy> <xsl:apply-templates select="@*|node()[1]" /> </xsl:copy> </xsl:template> </xsl:stylesheet> 

Conclusion:

 <list> <item label="(1)">some text</item> <item label="(2)"> <anotherNode>some text</anotherNode> <list> <item label="a">some text</item> <item label="b">some text</item> </list> </item> </list> 

In addition, this stylesheet:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:key name="kItemByFirstSibling" match="item[preceding-sibling::*[1][self::item]]" use="generate-id(preceding-sibling::item [not(preceding-sibling::*[1][self::item])][1])"/> <xsl:template match="@*|node()" name="identity"> <xsl:copy> <xsl:apply-templates select="@*|node()" /> </xsl:copy> </xsl:template> <xsl:template match="*[not(self::list)]/item"/> <xsl:template match="*[not(self::list)] /item[not(preceding-sibling::*[1][self::item])]" priority="1"> <list> <xsl:for-each select=".|key('kItemByFirstSibling',generate-id())"> <xsl:call-template name="identity"/> </xsl:for-each> </list> </xsl:template> </xsl:stylesheet> 

Note The first style sheet uses the finest-grained transversal (after the first item it will wrap any node). Repeated identity transformation of the second style sheet.

Change Addressing a new request, with a new input, output of both stylesheets:

 <list> <item label="(1)">some text</item> <item label="(2)"> <list> <item label="a">some text</item> <item label="b">some text</item> </list> <anotherNode>some text</anotherNode> <list> <item label="c">some text</item> <item label="d">some text</item> </list> </item> </list> 
+3
source

You did not address this in the original question, so this may not be required. But if the input has several sequences of <item> elements that need to be wrapped, which are separated from each other by other sibling elements, for example:

 <list> <item label="(1)">some text</item> <item label="(2)"> <item label="a">some text</item> <item label="b">some text</item> <anotherNode>some text</anotherNode> <item label="c">some text</item> <item label="d">some text</item> </item> </list> 

the previous answers, I think, combine the <item> elements together, changing their order:

 <list> <item label="(1)">some text</item> <item label="(2)"> <list> <!-- opening new wrapper node--> <item label="a">some text</item> <item label="b">some text</item> <item label="c">some text</item> <item label="d">some text</item> </list> <!-- closing new wrapper node--> <anotherNode>some text</anotherNode> </item> </list> 

Do you want it or want to wrap them separately?

 <list> <item label="(1)">some text</item> <item label="(2)"> <list> <!-- opening new wrapper node--> <item label="a">some text</item> <item label="b">some text</item> </list> <!-- closing new wrapper node--> <anotherNode>some text</anotherNode> <list> <!-- opening new wrapper node--> <item label="c">some text</item> <item label="d">some text</item> </list> <!-- closing new wrapper node--> </item> </list> 

If the latter is likely to be the easiest, use the XSLT 2.0 <xsl:for-each-group group-adjacent="name()" /> construct. I don't know if PHP 5 XSLT 2.0 is available, but if you can use such a thing, see this good article .

0
source

All Articles