如何复制xml元素

时间:2011-07-12 13:31:29

标签: xml xslt xpath xslt-1.0

我必须根据特定的id将xml有效负载复制到尽可能多的xml有效负载中,例如userid

<ns2:Details xmlns:ns2="ns">
  <ns2:var1>AA0511201143</ns2:var1>
  <ns2:var2>PARCEL</ns2:var2>
  <ns2:var3>04/04/2011</ns2:var3>
  <ns2:var4>Organization</ns2:var4>
  <ns2:UserId>46</ns2:UserId>
  <ns2:UserId>237</ns2:UserId>
</ns2:Details>

我需要输出

<ns2:Details>
  <ns2:var1>AA0511201143</ns2:var1>
  <ns2:var2>PARCEL</ns2:var2>
  <ns2:var3>04/04/2011</ns2:var3>
  <ns2:var4>Organization</ns2:var4>
  <ns2:UserId>46</ns2:UserId>
</ns2:Details>
<ns2:Details>
  <ns2:var1>AA0511201143</ns2:var1>
  <ns2:var2>PARCEL</ns2:var2>
  <ns2:var3>04/04/2011</ns2:var3>
  <ns2:var4>Organization</ns2:var4>
  <ns2:UserId>237</ns2:UserId>
</ns2:Details>

这是可能的


更新:以下给出的答案工作正常,但有一点我没注意到。如果用户标识相同且重复,则应显示相同的xml有效负载。为此,我尝试了以下方法来获取userid的独特元素

<xsl:param name="userId" select="ns0:UserId[generate-id(.)=generate-id(key('k', ns0:UserId)[1])]"/>

但这不起作用,也尝试使用上面的

..[generate-id(.)=generate-id(key('k', ns0:UserId)[1])] 

在模板级别也无法正常工作

我错过了什么吗?

更新: 我对上面的代码做了一个小修改,而不是在xsl:param上工作,我在xsl上使用它:apply-template

修改前的

(作为对我的回答) &lt; xsl:apply-templates select =“// ns2:Details / ns2:UserId”/&gt; 修改后 &lt; xsl:apply-templates select =“// ns2:Details / ns2:UserId [generate-id(。)= generate-id(key('myUserId',。)[1])]”/&gt;

我的错误我使用的是ns2:userid而不是“。”

完整的xsl代码 ---

<xsl:output method="xml" indent="yes"/> <xsl:key name="k" match="ns2:UserId" use="text()"/> <xsl:key name="myUserId" match="ns2:UserId" use="."/> <xsl:template match="/"> <ns2:Root> <xsl:apply-templates select="//ns2:Details/ns2:UserId[generate-id(.)=generate-id(key('myUserId', .)[1])]"/> </ns2:Root> </xsl:template>

<xsl:template match="//ns2:Details"> <xsl:param name="userId" select="ns2:UserId"/> <ns2:Details> <xsl:copy-of select="key('k', $userId)[1]"/> <!-- displays UserId values--> <xsl:copy-of select="./*[name() != 'ns2:UserId']"/> <!-- displays other values--> </ns2:Details> </xsl:template>

<xsl:template match="ns2:UserId"> <xsl:apply-templates select=".."> <xsl:with-param name="userId" select="."/> </xsl:apply-templates> </xsl:template>

请验证它。这也适合我...

7 个答案:

答案 0 :(得分:4)

假设XML:

<ns2:Details xmlns:ns2="ns2">
  <ns2:var1>AA0511201143</ns2:var1>
  <ns2:var2>PARCEL</ns2:var2>
  <ns2:var3>04/04/2011</ns2:var3>
  <ns2:var4>Organization</ns2:var4>
  <ns2:UserId>46</ns2:UserId>
  <ns2:UserId>237</ns2:UserId>
  <ns2:UserId>46</ns2:UserId>
</ns2:Details>

XSLT:

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

  <xsl:key name="k" match="ns2:UserId" use="text()"/>

  <xsl:template match="/">
    <root>
      <xsl:apply-templates select="//ns2:Details/ns2:UserId[not(node() = preceding-sibling::node())]"/>
    </root>
  </xsl:template>

  <xsl:template match="//ns2:Details">
    <xsl:param name="userId" select="ns2:UserId"/>

    <ns2:Details>
      <xsl:copy-of select="key('k', $userId)[not(node() = preceding-sibling::node())]"/>
      <xsl:copy-of select="./*[name() != 'ns2:UserId']"/>
    </ns2:Details>
  </xsl:template>

  <xsl:template match="ns2:UserId">
    <xsl:apply-templates select="..">
      <xsl:with-param name="userId" select="."/>
    </xsl:apply-templates>
  </xsl:template>

</xsl:stylesheet>

输出XML:

<?xml version="1.0" encoding="utf-8"?>
<root xmlns:ns2="ns2">
  <ns2:Details>
    <ns2:UserId>46</ns2:UserId>
    <ns2:var1>AA0511201143</ns2:var1>
    <ns2:var2>PARCEL</ns2:var2>
    <ns2:var3>04/04/2011</ns2:var3>
    <ns2:var4>Organization</ns2:var4>
  </ns2:Details>
  <ns2:Details>
    <ns2:UserId>237</ns2:UserId>
    <ns2:var1>AA0511201143</ns2:var1>
    <ns2:var2>PARCEL</ns2:var2>
    <ns2:var3>04/04/2011</ns2:var3>
    <ns2:var4>Organization</ns2:var4>
  </ns2:Details>
</root>

答案 1 :(得分:1)

此转化(简称,只有两个模板,没有xsl:for-each,没有模式):

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

 <xsl:key name="kIdByVal" match="ns2:UserId" use="."/>

 <xsl:template match="/">
  <xsl:apply-templates select=
  "ns2:Details/ns2:UserId
        [generate-id()=generate-id(key('kIdByVal',.)[1])]
  "/>
 </xsl:template>

 <xsl:template match="ns2:UserId">
  <ns2:Details>
   <xsl:copy-of select=
    "../node()
          [not(self::ns2:UserId
                 [not(generate-id()=generate-id(current()))])
          ]"/>
  </ns2:Details>
 </xsl:template>
</xsl:stylesheet>

应用于此XML文档(包含冗余的ns2:UserId元素):

<ns2:Details xmlns:ns2="ns">
    <ns2:var1>AA0511201143</ns2:var1>
    <ns2:var2>PARCEL</ns2:var2>
    <ns2:var3>04/04/2011</ns2:var3>
    <ns2:var4>Organization</ns2:var4>
    <ns2:UserId>46</ns2:UserId>
    <ns2:UserId>237</ns2:UserId>
    <ns2:UserId>46</ns2:UserId>
</ns2:Details>

产生完全正确的结果

<ns2:Details xmlns:ns2="ns">
   <ns2:var1>AA0511201143</ns2:var1>
   <ns2:var2>PARCEL</ns2:var2>
   <ns2:var3>04/04/2011</ns2:var3>
   <ns2:var4>Organization</ns2:var4>
   <ns2:UserId>46</ns2:UserId>
</ns2:Details>
<ns2:Details xmlns:ns2="ns">
   <ns2:var1>AA0511201143</ns2:var1>
   <ns2:var2>PARCEL</ns2:var2>
   <ns2:var3>04/04/2011</ns2:var3>
   <ns2:var4>Organization</ns2:var4>
   <ns2:UserId>237</ns2:UserId>
</ns2:Details>

解释:Muenchian分组,xsl:copy-of,使用current()

答案 2 :(得分:0)

是的,这是可能的。您可以使用for for-each loop使用for-each ns2:UserID节点进行循环。

答案 3 :(得分:0)

实现所需结果的一种方法是使用身份转换并覆盖ns2:Details节点。

在覆盖模板中,您可以使用重复指令xsl:for-each来迭代所有UserId

要管理重复的UserId,您可以使用来自Menuchian分组方法的众所周知的谓词。

因为我们将使用身份转换,所以生成所有内容的方式要简单得多。

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

    <xsl:key name="UserId" match="ns2:UserId" use="."/>

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

    <xsl:template match="ns2:Details">
        <xsl:for-each select="ns2:UserId
                      [generate-id()
                       = generate-id(key('UserId',.)[1])]">
            <ns2:Details>
                <xsl:copy-of select="../@*"/>
                <xsl:apply-templates select="../node()
                    [not(self::ns2:UserId)]"/>
                <xsl:apply-templates select="."/>
            </ns2:Details>
        </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>

当此变换应用于问题中提供的输入时,将获得以下片段:

<ns2:Details xmlns:ns2="ns">
   <ns2:var1>AA0511201143</ns2:var1>
   <ns2:var2>PARCEL</ns2:var2>
   <ns2:var3>04/04/2011</ns2:var3>
   <ns2:var4>Organization</ns2:var4>
   <ns2:UserId>46</ns2:UserId>
</ns2:Details>
<ns2:Details xmlns:ns2="ns">
   <ns2:var1>AA0511201143</ns2:var1>
   <ns2:var2>PARCEL</ns2:var2>
   <ns2:var3>04/04/2011</ns2:var3>
   <ns2:var4>Organization</ns2:var4>
   <ns2:UserId>237</ns2:UserId>
</ns2:Details>

即使输入文档中存在重复的UserId,也会获得此输出。

答案 4 :(得分:0)

以下样式表处理重复项:

<xsl:stylesheet version="1.0" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xmlns:ns2="ns2">
    <xsl:output method="xml" indent="yes" />
    <xsl:key name="byUserId" match="ns2:UserId" use="." />
    <xsl:template match="/">
        <root>
            <xsl:apply-templates
                select="ns2:Details/ns2:UserId
                    [generate-id()=generate-id(key('byUserId', .)[1])]" />
        </root>
    </xsl:template>
    <xsl:template match="ns2:UserId">
        <xsl:apply-templates select=".." mode="out">
            <xsl:with-param name="userId" select="." />
        </xsl:apply-templates>
    </xsl:template>
    <xsl:template match="ns2:Details" mode="out">
        <xsl:param name="userId" select="''" />
        <xsl:copy>
            <xsl:apply-templates select="node()|@*" mode="out" />
            <xsl:copy-of select="$userId"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="ns2:UserId" mode="out" />
    <xsl:template match="node()|@*" mode="out">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*" mode="out" />
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

在此输入上:

<ns2:Details xmlns:ns2="ns2">
    <ns2:var1>AA0511201143</ns2:var1>
    <ns2:var2>PARCEL</ns2:var2>
    <ns2:var3>04/04/2011</ns2:var3>
    <ns2:var4>Organization</ns2:var4>
    <ns2:UserId>46</ns2:UserId>
    <ns2:UserId>237</ns2:UserId>
    <ns2:UserId>46</ns2:UserId>
</ns2:Details>

产地:

<root xmlns:ns2="ns2">
    <ns2:Details>
        <ns2:var1>AA0511201143</ns2:var1>
        <ns2:var2>PARCEL</ns2:var2>
        <ns2:var3>04/04/2011</ns2:var3>
        <ns2:var4>Organization</ns2:var4>
        <ns2:UserId>46</ns2:UserId>
    </ns2:Details>
    <ns2:Details>
        <ns2:var1>AA0511201143</ns2:var1>
        <ns2:var2>PARCEL</ns2:var2>
        <ns2:var3>04/04/2011</ns2:var3>
        <ns2:var4>Organization</ns2:var4>
        <ns2:UserId>237</ns2:UserId>
    </ns2:Details>
</root>

答案 5 :(得分:0)

除了在XSLT 2.0中,它很简单而不是使用apply-templates编写多个temlates,使用单个模板查看下面的代码,它将得到相同的结果。

    <xsl:for-each-group select="*:Details" group-by="*:UserId">


        <xsl:comment select="current-grouping-key()"/>


        <ns2:Details>


            <xsl:for-each select="current-group()">


                <xsl:element name="ns2:UserId">


                    <xsl:value-of select="current-grouping-key()"/>
                </xsl:element>


                <xsl:copy-of select="*[name() != 'ns2:UserId']"/>


            </xsl:for-each>


        </ns2:Details>


    </xsl:for-each-group>


</xsl:template>

答案 6 :(得分:0)

XSLT 1.0上还有两个紧凑版本,结果相同。

  1. 基于generate-id()

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  xmlns:ns2="ns">
    
        <xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
        <xsl:strip-space elements="*"/>
    
        <xsl:key name="k1" match="/ns2:Details/ns2:UserId" use="."/>
        <xsl:key name="k2" match="/ns2:Details/ns2:UserId" use="generate-id() = generate-id(key('k1',.)[1])"/>
    
        <xsl:template match = "/">
            <xsl:for-each select="key('k2',true())">
                <ns:Details>
                    <xsl:copy-of select="../node()[not(self::ns2:UserId)]"></xsl:copy-of>
                    <xsl:copy-of select="."/>
                </ns:Details>
            </xsl:for-each>
        </xsl:template>
    
    </xsl:stylesheet>
    
  2. 基于先前的兄弟姐妹

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  xmlns:ns2="ns">
    
        <xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
        <xsl:strip-space elements="*"/>
    
        <xsl:key name="k" match="/ns2:Details/ns2:UserId" use="not(node() = preceding-sibling::ns2:UserId/node())"/>
    
        <xsl:template match = "/">
            <xsl:for-each select="key('k',true())">
                <ns:Details>
                    <xsl:copy-of select="../node()[not(self::ns2:UserId)]"></xsl:copy-of>
                    <xsl:copy-of select="."/>
                </ns:Details>
            </xsl:for-each>
        </xsl:template>
    
    </xsl:stylesheet>