1

我正在使用 XSLT 转换从 web 派生的 XML,并将其即时转换为表示为 output 的目标 xml 文件。即使尝试了很多,我仍然无法这样做,任何人都可以帮助我进行这种转换。

源 XML

<allocelement>
    <hd1>12</hd1>
    <hd2>14</hd2>
    <hd3>87</hd3>
        <alc>1</alc>
    <amount>4587</amount>
    <code>1111</code>
</allocelement>
<alloclement>
        <hd1>12</hd1>
    <hd2>14</hd2>
    <hd3>87</hd3>
    <alc>2</alc>
    <amount>80000</amount>
    <code>1111</code>
</alloclement>
<alloclement>
    <hd1>875</hd1>
    <hd2>455</hd2>
    <hd3>455</hd3>
    <alc>2</alc>
    <amount>80000</amount>
    <code>1112</code>
 </alloclement>

所需输出

<allocelement>
    <Codeheader>
    <code>1111</code>
        <hd1>12</hd1>
        <hd2>14</hd2>
        <hd3>87</hd3>
                <alc>1</alc>
                    <amount>4587</amount>
                <alc>2</alc>
                    <amount>80000</amount>
    </codeHeader>
        <CodeHeader>
    <code>1112</code>
        <hd1>875</hd1>
        <hd2>455</hd2>
        <hd3>455</hd3>
            <alc>2</alc>
              <amount>80000</amount>
    </CodeHeader>
</allocelement>

分组是基于 Code,[hd1,hd2,hd3] 这样具有相同 Code 和 [hd1,hd2,hd3] 的不同元素将被合并,并且只显示不同的字段,即。和。我也在使用 xslt 1.0 。

4

2 回答 2

0

我假设您的输入 XML 有一个root节点,该节点将所有内容包装起来以制作格式良好的文档。我还假设所有allocelement的拼写都是正确的(你有一些拼写为 as alloclement)。我还假设您要删除由标签名称及其文本值标识的重复项。

使用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="code" match="allocelement/code" use="."/>

    <xsl:key name="code-sibling" 
             match="allocelement/*[name() != 'code']" 
             use="concat(parent::*/code, '|', name(), '|', .)"/>

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

    <xsl:template match="/">
        <xsl:apply-templates select="root/allocelement[1]"/>
    </xsl:template>

    <xsl:template match="allocelement">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates 
                    select="(. | following-sibling::allocelement)/code
                                [generate-id() = generate-id(key('code', .)[1])]"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="code">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:value-of select="."/>

            <xsl:apply-templates 
                    select="key('code', .)/(preceding-sibling::* | following-sibling::*)
                                [generate-id() = 
                                 generate-id(key('code-sibling', concat(parent::*/code, '|', name(), '|', .))[1])]"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

当应用于:

<root>
    <allocelement>
        <hd1>12</hd1>
        <hd2>14</hd2>
        <hd3>87</hd3>
        <alc>1</alc>
        <amount>4587</amount>
        <code>1111</code>
    </allocelement>
    <allocelement>
        <hd1>12</hd1>
        <hd2>14</hd2>
        <hd3>87</hd3>
        <alc>2</alc>
        <amount>80000</amount>
        <code>1111</code>
    </allocelement>
    <allocelement>
        <hd1>875</hd1>
        <hd2>455</hd2>
        <hd3>455</hd3>
        <alc>2</alc>
        <amount>80000</amount>
        <code>1112</code>
     </allocelement>
</root>

产生:

<root>
   <allocelement>
      <code>1111<hd1>12</hd1>
         <hd2>14</hd2>
         <hd3>87</hd3>
         <alc>1</alc>
         <amount>4587</amount>
         <alc>2</alc>
         <amount>80000</amount>
      </code>
      <code>1112<hd1>875</hd1>
         <hd2>455</hd2>
         <hd3>455</hd3>
         <alc>2</alc>
         <amount>80000</amount>
      </code>
   </allocelement>
</root>

这是它的工作原理。默认路由是身份转换。我们首先中断allocelement并将转换发送到不同的路线。我们让它复制唯一code元素(由元素的文本值标识的唯一性)及其值,然后在code共享相同值的所有节点的所有前/后兄弟姐妹上应用模板。这些现在将是节点的子节点code。然后我们为那些唯一的兄弟姐妹(属于code相同的值,具有相同的名称和相同的文本值)调用身份转换。

一注。混合内容从来都不是一个好主意。看看你是否真的需要在code.

使用XSLT 2.0,您可以远离generate-id()s:

<xsl:template match="allocelement">
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <xsl:for-each-group select="(. | following-sibling::allocelement)/code" group-by=".">
            <xsl:apply-templates select="."/>
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template>

<xsl:template match="code">
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <xsl:value-of select="."/>

        <xsl:for-each-group select="key('code', .)/(preceding-sibling::* | following-sibling::*)"
                            group-by="concat(parent::*/code, '|', name(), '|', .)">
            <xsl:apply-templates select="."/>
        </xsl:for-each-group> 
    </xsl:copy>
</xsl:template>
于 2012-05-29T14:14:07.867 回答
0

一个更短更简单的 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="kCodeByVal" match="code" use="."/>

 <xsl:template match="/*">
  <allocelement>
   <xsl:apply-templates/>
  </allocelement>
 </xsl:template>

<xsl:template match="allocelement"/>

 <xsl:template match=
  "allocelement
    [generate-id(code) = generate-id(key('kCodeByVal', code)[1])]">
  <code><xsl:value-of select="code"/>
    <xsl:copy-of select=
    "node()[not(self::code)]
    | key('kCodeByVal', code)/../*[self::alc or self::amount]"/>
  </code>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于以下 XML 文档(包装提供的 XML 片段的单个顶部元素)时:

<t>
    <allocelement>
        <hd1>12</hd1>
        <hd2>14</hd2>
        <hd3>87</hd3>
        <alc>1</alc>
        <amount>4587</amount>
        <code>1111</code>
    </allocelement>
    <allocelement>
        <hd1>12</hd1>
        <hd2>14</hd2>
        <hd3>87</hd3>
        <alc>2</alc>
        <amount>80000</amount>
        <code>1111</code>
    </allocelement>
    <allocelement>
        <hd1>875</hd1>
        <hd2>455</hd2>
        <hd3>455</hd3>
        <alc>2</alc>
        <amount>80000</amount>
        <code>1112</code>
    </allocelement>
</t>

产生了想要的正确结果

<allocelement>
   <code>1111<hd1>12</hd1>
      <hd2>14</hd2>
      <hd3>87</hd3>
      <alc>1</alc>
      <amount>4587</amount>
      <alc>2</alc>
      <amount>80000</amount>
   </code>
   <code>1112<hd1>875</hd1>
      <hd2>455</hd2>
      <hd3>455</hd3>
      <alc>2</alc>
      <amount>80000</amount>
   </code>
</allocelement>

解释:正确使用孟池分组法


二、XSLT 2.0 解决方案——同样更短,更重要的是——在语法和语义上正确

<xsl:stylesheet version="2.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="/*">
  <allocelement>
   <xsl:for-each-group select="*" group-by="code">
     <code><xsl:value-of select="code"/>
        <xsl:sequence select=
        "node()[not(self::code)]
        | current-group()/*[self::alc or self::amount]"/>
  </code>
   </xsl:for-each-group>
  </allocelement>
 </xsl:template>
</xsl:stylesheet>

更新:OP 改变了对输出的要求。

这是相应的修改后的 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="kCodeByVal" match="code" use="."/>

     <xsl:template match="/*">
      <allocelement>
       <xsl:apply-templates/>
      </allocelement>
     </xsl:template>

    <xsl:template match="allocelement"/>

     <xsl:template match=
      "allocelement
        [generate-id(code) = generate-id(key('kCodeByVal', code)[1])]">
      <codeHeader>
       <code><xsl:value-of select="code"/></code>
         <xsl:copy-of select=
         "node()[not(self::code)]
         | key('kCodeByVal', code)/../*[self::alc or self::amount]"/>
      </codeHeader>
     </xsl:template>
</xsl:stylesheet>
于 2012-05-30T04:05:54.573 回答