XSL combining values of siblings if values of an attribute is same

微笑、不失礼 提交于 2020-02-15 06:40:22

问题


This is how my XML looks like

<?xml version="1.0"?>
<Nodes>
<NodeA NodeAattr="123">

<NodeB NodeBattr="456"></NodeB>

  <NodeC>
     <NodeD NodeDAttr="ValueD">
        <NodeE Name="ValueABC"> "555" </NodeE >
        <NodeE Name="ValueABC"> "666" </NodeE>
     </NodeD>
  </NodeC>
</NodeA>
</Nodes>

If the values of the Name attribute of NodeE are same, concatenate the values of NodeE. And my final output xml has to look like

<NodeA NodeAattr="123">

<NodeB NodeBattr="456"></NodeB>

  <NodeC>
     <NodeD="ValueD">
        <NodeE Name="ValueABC"> "555" , "666" </NodeE >
     </NodeD>
  </NodeC>
</NodeA>

Please provide me with the xsl.. I am using XSLT1.0


回答1:


This should do it:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
  <xsl:strip-space elements="*" />

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="NodeE">
    <xsl:copy>
      <xsl:apply-templates select="@* | text()" />
      <xsl:call-template name="NextSibling" />
    </xsl:copy>
  </xsl:template>
  <xsl:template match="NodeE[@Name = preceding-sibling::*[1][self::NodeE]/@Name]" />

  <xsl:template match="NodeE" mode="includeSib">
    <xsl:value-of select="concat(',', .)"/>
    <xsl:call-template name="NextSibling" />
  </xsl:template>

  <xsl:template name="NextSibling">
    <xsl:apply-templates
      select="following-sibling::*[1]
                                  [self::NodeE and @Name = current()/@Name]"
      mode="includeSib" />
  </xsl:template>

</xsl:stylesheet>

When run on this input (with a few additional values to demonstrate its functionality):

<Nodes>
  <NodeA NodeAattr="123">

    <NodeB NodeBattr="456"></NodeB>

    <NodeC>
      <NodeD NodeDAttr="ValueD">
        <NodeE Name="ValueABC"> "555" </NodeE >
        <NodeE Name="ValueABC"> "666" </NodeE>
        <NodeE Name="ValueDEF"> "555" </NodeE >
        <NodeE Name="ValueDEF"> "565" </NodeE >
        <NodeE Name="ValueDEF"> "575" </NodeE >
        <NodeE Name="ValueABC"> "595" </NodeE >
      </NodeD>
    </NodeC>
  </NodeA>
</Nodes>

The result is:

<Nodes>
  <NodeA NodeAattr="123">
    <NodeB NodeBattr="456" />
    <NodeC>
      <NodeD NodeDAttr="ValueD">
        <NodeE Name="ValueABC"> "555" , "666" </NodeE>
        <NodeE Name="ValueDEF"> "555" , "565" , "575" </NodeE>
        <NodeE Name="ValueABC"> "595" </NodeE>
      </NodeD>
    </NodeC>
  </NodeA>
</Nodes>



回答2:


Here's a solution based on my answer to question #825783:

Stylesheet

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:key name="kNode" match="NodeE" use="@Name"/>

  <!--
  Identity transform: copy elements and attributes from input file as is
  -->
  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
  </xsl:template>

  <!--
  Use Muenchian grouping to apply unique NodeE elements.
  See http://www.jenitennison.com/xslt/grouping/muenchian.html
  -->
  <xsl:template match="NodeE[generate-id() = 
                       generate-id(key('kNode', @Name)[1])]">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <!--
      Apply <NodeE> elements with the same @Name attribute value as the current
      element with the "concat" mode enabled
      -->
      <xsl:apply-templates select="key('kNode', @Name)" mode="concat"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="NodeE" mode="concat">
    <xsl:value-of select="."/>
    <!-- Add comma except if this is the last node -->
    <xsl:if test="position() != last()">
      <xsl:text>, </xsl:text>
    </xsl:if>
  </xsl:template>

  <!-- Drop other <NodeE> elements -->
  <xsl:template match="NodeE"/>

</xsl:stylesheet>

Input

Using the input provided by JLRishe:

<Nodes>
  <NodeA NodeAattr="123">

    <NodeB NodeBattr="456"></NodeB>

    <NodeC>
      <NodeD NodeDAttr="ValueD">
        <NodeE Name="ValueABC"> "555" </NodeE>
        <NodeE Name="ValueABC"> "666" </NodeE>
        <NodeE Name="ValueDEF"> "555" </NodeE>
        <NodeE Name="ValueDEF"> "565" </NodeE>
        <NodeE Name="ValueDEF"> "575" </NodeE>
        <NodeE Name="ValueABC"> "595" </NodeE>
      </NodeD>
    </NodeC>
  </NodeA>
</Nodes>

Output

This is different from JLRishe's output because I understood the requirement differently:

<?xml version="1.0" encoding="utf-8"?>
<Nodes>
  <NodeA NodeAattr="123">
    <NodeB NodeBattr="456"/>
    <NodeC>
      <NodeD NodeDAttr="ValueD">
        <NodeE Name="ValueABC"> "555" ,  "666" ,  "595" </NodeE>
        <NodeE Name="ValueDEF"> "555" ,  "565" ,  "575" </NodeE>
      </NodeD>
    </NodeC>
  </NodeA>
</Nodes>


来源:https://stackoverflow.com/questions/16020026/xsl-combining-values-of-siblings-if-values-of-an-attribute-is-same

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!