如何使用xsl从具有相同名称的多个节点创建一个节点?

时间:2013-03-26 09:23:30

标签: xml xslt

我的XML结构就像

<Client>
  <Document>
    <service class="ABC">
      <author name="x"/>
      <Details>
        ...
      </Details>
    </service>
   </Document>
   <Document>
     <service class="ABC">
       <author name="x"/>
       <Details>
         ...
       </Details>
     </service>
   </Document>
   <Document>
     <service class="ABC">
       <author name="x"/>
       <Details>
         ...
       </Details>
     </service>
   </Document>'
</Client>

我希望它像

<Client>
  <Document>
    <service class="ABC">
      <author name="x"/>
       <Details>
         ...
       </Details>
    </service>
   </Document>
   <Document>
     <service class="ABC">
       <author name="x"/>
       <Details>
         ...
       </Details>
     </service>
   </Document>
   <Document>
     <service class="ABC">
       <author name="x"/>
       <Details>
         ...
       </Details>
     </service>
   </Document>'
   <component>
     <service class="ABC">
       <author name="x"/>
     </service>
   </component>'
</Client>

我的xsl文件包含

<xsl:template match="//v3:Client">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
      <component>
        <service class="">
          <xsl:apply-templates select="//v3:Client/v3:Document/v3:service/v3:author"/>
        </service>
      </component>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="//v3:Client/v3:component/v3:service">
    <xsl:element name="service" namespace="urn:hl7-org:v3">
      <xsl:attribute name="class">
        <xsl:value-of select="//v3:Client/v3:Document/v3:service/@class"/>
      </xsl:attribute>
    </xsl:element>
  </xsl:template>

但它没有给出理想的输出。它的输出类似于

Client
  Document
    service class="ABC"
      author name="x"
      details
        ...
      details
     service
    Document
   Document
    service class="ABC"
      author name="x"
      details
        ...
      details
    service
  Document
  Document
    service class="ABC"
      author name="x"
      details
        ...
      details
    service
  Document
  component
    service class=""
      author name="x"
    service
    service class=""
      author name="x"
    service
    service class=""
      author name="x"
    service
  component
Client

我是xsl的新手。有人可以帮我解决这个问题吗?

3 个答案:

答案 0 :(得分:0)

使用group-by功能:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" indent="yes" />

  <xsl:template match="/Client">

    <xsl:copy-of select="Document" />

    <!-- Group nodes by attribute value, produce one <component>
         per each group -->
    <xsl:for-each-group select="Document" group-by="service/@class">
      <component>
        <xsl:copy-of select="./service" />
      </component>
    </xsl:for-each-group>

  </xsl:template>

</xsl:stylesheet>

上面的样式表为每个<component>属性值生成一个Document class=""

答案 1 :(得分:0)

如果您无法访问group-by,则可以考虑使用following-sibling::轴。

换句话说,你正在寻找这样的东西:

<component>
   <xsl:apply-templates select="Document/service[@class != following-sibling::service/@class]" mode="component" />
</component>

<xsl:template match="service" mode="component">
     <service class="{@class}">
         <!-- etc. -->
     </service>
</xsl:template>

答案 2 :(得分:0)

如果要在组件元素中输出不同的服务元素,那么在XSTL1.0中,您需要使用一种名为 Muenchian分组的技术。我假设属性唯一标识一个服务,因此您首先要定义一个键来查找服务,如此

<xsl:key name="service" match="service" use="@class" />

然后,在创建组件元素时,要获取不同的服务元素,您需要查看所有这些元素,但只选择键中首先出现的元素对于他们的属性。

<xsl:apply-templates 
     select="Document/service[generate-id() = generate-id(key('service', @class)[1])]" 
     mode="component" />

在与服务元素匹配的模板中,您可以根据需要输出详细信息。在您的示例中,您似乎只需要作者元素,而不是详细信息

<xsl:template match="service" mode="component">
   <xsl:copy>
     <xsl:apply-templates select="author|@*"/>
   </xsl:copy>
</xsl:template>

模式的使用是因为您只想在输出compenent元素时匹配此模板。

这是完整的XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:key name="service" match="service" use="@class" />

<xsl:template match="Client">
   <xsl:copy>
     <xsl:apply-templates select="node()|@*"/>
     <component>
        <xsl:apply-templates select="Document/service[generate-id() = generate-id(key('service', @class)[1])]" mode="component" />
     </component>
   </xsl:copy>
</xsl:template>

<xsl:template match="service" mode="component">
    <xsl:copy>
      <xsl:apply-templates select="author|@*"/>
    </xsl:copy>
</xsl:template>

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

当应用于您的示例XML时,输出以下内容

<Client>
   <Document>
      <service class="ABC">
         <author name="x"/>
         <Details> ... </Details>
      </service>
   </Document>
   <Document>
      <service class="ABC">
         <author name="x"/>
         <Details> ... </Details>
      </service>
   </Document>
   <Document>
      <service class="ABC">
         <author name="x"/>
         <Details> ... </Details>
      </service>
   </Document>
   <component>
      <service class="ABC">
         <author name="x"/>
      </service>
   </component>
</Client>