2

我想使用 XSLT 1.0 将其中一个 xml 转换为主详细信息格式。我尝试使用此处的一些帖子获得见解,但无法正确理解。这里 MsgID 和 PartID 形成了唯一键。

源 XML:

<Parts>
 <Part>
  <MsgID>ABNHH877JJ</MsgID>
  <PartID>10</PartID>
  <Attr1>Part10-Attr1</Attr1>
  <Attr2>Part10-Attr2</Attr2>
 </Part>
 <Part>
  <MsgID>ABNHH877JJIUJ1</MsgID>
  <PartID>10</PartID>
  <Attr1>Part10-I-Attr1</Attr1>
  <Attr2>Part10-I-Attr2</Attr2>
 </Part>
 <Part>
  <MsgID>ABNHH877JJGHJ</MsgID>
  <PartID>20</PartID>
  <Attr1>Part20-Attr1</Attr1>
  <Attr2>Part20-Attr2</Attr2>
 </Part>
</Parts>

所需的目标 XML:

<Parts>
 <Part>
  <MsgID>ABNHH877JJ</MsgID>
  <PartID>10</PartID>
  <Attrs>
       <Attr1>Part10-Attr1</Attr1>
       <Attr2>Part10-Attr2</Attr2>
  </Attrs>
    <Attrs>
       <Attr1>Part10-I-Attr1</Attr1>
       <Attr2>Part10-I-Attr2</Attr2>
  </Attrs>
 </Part>
 <Part>
  <MsgID>ABNHH877JJGHJ</MsgID>
  <PartID>20</PartID>
  <Attrs>
     <Attr1>Part20-Attr1</Attr1>
    <Attr2>Part20-Attr2</Attr2>
  </Attrs>  
 </Part>
</Parts>
4

3 回答 3

2

一个正确、简短和高效的 XSLT 1.0 解决方案

<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="kPartById" match="Part" use="PartID"/>

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

 <xsl:template match="Part[generate-id()=generate-id(key('kPartById', PartID)[1])]">
  <Part>
    <xsl:apply-templates select="*[not(starts-with(name(), 'Attr'))]"/>
    <xsl:apply-templates mode="attr" select="key('kPartById', PartID)"/>
  </Part>
 </xsl:template>

 <xsl:template match="Part" mode="attr">
  <attrs>
    <xsl:apply-templates select="*[starts-with(name(), 'Attr')]"/>
  </attrs>
 </xsl:template>
 <xsl:template match="Part"/>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时:

<Parts>
    <Part>
        <MsgID>ABNHH877JJ</MsgID>
        <PartID>10</PartID>
        <Attr1>Part10-Attr1</Attr1>
        <Attr2>Part10-Attr2</Attr2>
    </Part>
    <Part>
        <MsgID>ABNHH877JJIUJ1</MsgID>
        <PartID>10</PartID>
        <Attr1>Part10-I-Attr1</Attr1>
        <Attr2>Part10-I-Attr2</Attr2>
    </Part>
    <Part>
        <MsgID>ABNHH877JJGHJ</MsgID>
        <PartID>20</PartID>
        <Attr1>Part20-Attr1</Attr1>
        <Attr2>Part20-Attr2</Attr2>
    </Part>
</Parts>

产生了想要的正确结果:

<Parts>
   <Part>
      <MsgID>ABNHH877JJ</MsgID>
      <PartID>10</PartID>
      <attrs>
         <Attr1>Part10-Attr1</Attr1>
         <Attr2>Part10-Attr2</Attr2>
      </attrs>
      <attrs>
         <Attr1>Part10-I-Attr1</Attr1>
         <Attr2>Part10-I-Attr2</Attr2>
      </attrs>
   </Part>
   <Part>
      <MsgID>ABNHH877JJGHJ</MsgID>
      <PartID>20</PartID>
      <attrs>
         <Attr1>Part20-Attr1</Attr1>
         <Attr2>Part20-Attr2</Attr2>
      </attrs>
   </Part>
</Parts>
于 2013-01-03T13:56:04.843 回答
2

更正的 XML:

<?xml version="1.0" encoding="utf-8"?>
<Parts>
  <Part>
    <MsgID>ABNHH877JJ</MsgID>
    <PartID>10</PartID>
    <Attr1>Part10-Attr1</Attr1>
    <Attr2>Part10-Attr2</Attr2>
  </Part>
  <Part>
    <MsgID>ABNHH877JJ</MsgID>
    <PartID>10</PartID>
    <Attr1>Part10-I-Attr1</Attr1>
    <Attr2>Part10-I-Attr2</Attr2>
  </Part>
  <Part>
    <MsgID>ABNHH877JJGHJ</MsgID>
    <PartID>20</PartID>
    <Attr1>Part20-Attr1</Attr1>
    <Attr2>Part20-Attr2</Attr2>
  </Part>
</Parts>

XSLT 代码:

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

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

  <xsl:template match="/Parts/Part">
    <xsl:copy>
      <xsl:apply-templates select="MsgID|PartID"/>
      <xsl:element name="Attrs">
        <xsl:apply-templates select="Attr1|Attr2"/>
      </xsl:element>

      <xsl:for-each select="following-sibling::Part[MsgID= current()/MsgID and PartID= current()/PartID]">
      <xsl:element name="Attrs">
        <xsl:apply-templates select="Attr1|Attr2"/>
      </xsl:element>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/Parts/Part[MsgID = preceding-sibling::Part/MsgID and PartID = preceding-sibling::Part/PartID]"/>

</xsl:stylesheet>

结果:

<?xml version="1.0" encoding="utf-8"?>
<Parts>
  <Part>
    <MsgID>ABNHH877JJ</MsgID>
    <Attrs>
      <Attr1>Part10-Attr1</Attr1>
      <Attr2>Part10-Attr2</Attr2>
    </Attrs>
    <Attrs>
      <Attr1>Part10-I-Attr1</Attr1>
      <Attr2>Part10-I-Attr2</Attr2>
    </Attrs>
  </Part>

  <Part>
    <MsgID>ABNHH877JJGHJ</MsgID>
    <Attrs>
      <Attr1>Part20-Attr1</Attr1>
      <Attr2>Part20-Attr2</Attr2>
    </Attrs>
  </Part>
</Parts>
于 2013-01-03T11:53:30.733 回答
1

我一直在寻找这种转换(master-detail),得到了不同的解决方案,在这里分享给大家。

这就是想法,我需要打破 2 个字段 Key1 和 Key2(可能是一个或多个),并且我只需要制作一次 Header 和每个 entry-times 的详细信息。

<!-- start -->

<xsl:template match="/">

   <xsl:for-each select="/parentNode/entry">   
      <!-- Current Key values -->
      <xsl:variable name="Key1"><xsl:value-of select="properties/Key1"/></xsl:variable>
      <xsl:variable name="Key2"><xsl:value-of select="properties/Key2"/></xsl:variable>

      <!-- Previous key values -->
      <xsl:variable name="PrevKey1"><xsl:value-of select="preceding-sibling::entry[1]/properties/Key1"></xsl:value-of></xsl:variable>
      <xsl:variable name="PrevKey2"><xsl:value-of select="preceding-sibling::entry[1]/properties/Key2"></xsl:value-of></xsl:variable>

       <!-- Compare Values, if differents then put the header -->
       <xsl:if test="$PrevKey1 != $Key or $PrevKey2 != $Key">
            <!-- Put here whatever you want for the header, just this time -->
       </xsl:if>

       <!-- Put here whatever you want for the details, every iteration -->

   </xsl:for-each>

</xsl:template>
<!-- ends -->
于 2014-01-24T20:57:22.733 回答