繁体   English   中英

使用xslt合并和重新排列xml

[英]Merging and rearranging xml using xslt

我正在使用XSLT转换从Web派生的XML,并迅速将其转换为目标XML文件,表示为output。 即使做了很多尝试,我仍然无法执行此操作。有人可以帮我完成此转换吗?

源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。

我假设您的输入XML具有一个根节点,该root将所有XML包裹起来以构成格式良好的文档。 我还要假设所有allocelement拼写正确(您有一些拼写为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()

<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>

一个明显更短,更简单的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>

说明 :正确使用Muenchian分组方法


二。 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>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM