问题
I need to merge the two nodes street as they have the same:
- father node: city NEW YORK
- same method: modify
- same id: 0
Attributes values must be merged (See the output file at the end of this post)
Here is the input file:
<country>
<state id="NEW JERSEY">
<city id="NEW YORK">
<district id="BRONX" method="modify">
<street id="0" method="modify">
<attributes>
<temperature>98</temperature>
<altitude>1300</altitude>
</attributes>
</street>
<dadada id="99" method="modify" />
<street id="0" method="modify">
<attributes>
<temperature>80</temperature>
<streetnumber> 67 </streetnumber>
</attributes>
</street>
<dididi id="432" method="modify" />
</district>
</city>
</state>
Expected output:
<country>
<state id="NEW JERSEY">
<city id="NEW YORK">
<district id="BRONX" method="modify">
<street id="0" method="modify">
<attributes>
<temperature>80</temperature>
<altitude>1300</altitude>
<streetnumber> 67 </streetnumber>
</attributes>
</street>
<dadada id="99" method="modify" />
<dididi id="432" method="modify" />
</district>
</city>
</state>
</country>
Please help, I am just beginning XSLT
回答1:
I have assumed that you are interested in an XSLT 2.0, because that is how you have tagged your question. Let me know if you need the XSLT 1.0 equivalent. This XSLT 2.0 style-sheet should do the trick ...
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[street]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:for-each-group select="street" group-by="@method">
<xsl:apply-templates select="current-group()[1]" />
</xsl:for-each-group>
<xsl:apply-templates select="node()[not(self::street)]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="street/attributes">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:variable name="grouped-method" select="../@method" />
<xsl:for-each-group select="../../street[@method=$grouped-method]/attributes/*" group-by="name()">
<xsl:apply-templates select="current-group()[1]" />
</xsl:for-each-group>
<xsl:apply-templates select="comment()|processing-instruction()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Explanation
The second template, matching on elements which are parents of streets, will group the child streets by common method. For each group, only the first street in the group is copied. The rest are dropped.
When this first street of the group is having its 'attributes' node process in the third template, we merge all the attributes from the same group. Possibly 'attributes' is an unfortunate element name in an XML document! This grouping is achieved by looking at all the 'attributes' child nodes of all the allied streets which have the same street parent (Bronx district) and grouping by element name. If there are multiple elements in such a group, just take the values from the first one.
I'm not sure that this is exactly what you want because although the street attributes are merged by 'father' node (Bronx), they are not merged at the city level. This reflects the ambiguity in your question. The 'father' node of streets in your sample date is district not city. If I have got this wrong, and you want grouping at the city level, please clarify and update your question.
来源:https://stackoverflow.com/questions/11426423/how-to-merge-two-nodes-having-the-same-father-the-same-method-and-the-same-id