【问题标题】:How to Duplicate xml elements如何复制 xml 元素
【发布时间】:2011-10-03 15:29:20
【问题描述】:

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

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

这可能吗


更新:以下给出的答案运行良好,但有一个小问题我没有提及。如果用户 ID 相同且重复,则应显示相同的 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 使用它

修改前(作为回答我提供) 修改后

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

完整的 xsl 代码 ---

根> 模板>

请确认。这也对我有用...

【问题讨论】:

  • 您的输入和期望的输出格式不正确。我假设“Organization”应该是“Organization”?
  • +1 很好的问题。不太了解您的更新。你想重复UserId 生成其他Details 吗?我的回答对你有用吗?
  • 好的,我明白了,更新了我的答案以删除重复项。
  • 好问题,+1。到目前为止,请参阅我对最短(行数、模板数)和最简单(无模式、无 xsl:for-each、无参数)解决方案的回答。 :) 此外,还提供了解决方案中关键时刻的简要说明。
  • @anvv,我更新了我的答案以删除冗余节点。

标签: xml xslt xpath xslt-1.0


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    除了在 XSLT 2.0 中,它很简单而不是使用 apply-templates 编写多个模板,请参见下面的带有单个模板的代码,它将给出相同的结果。

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

    【讨论】:

      【解决方案3】:

      实现所需结果的一种方法是使用 身份转换 并覆盖 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】:

        假定的 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>
        

        【讨论】:

          【解决方案5】:

          这个转换(简而言之,只有两个模板,没有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>
          

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

          【讨论】:

            【解决方案6】:

            以下样式表处理重复:

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

            【讨论】:

              【解决方案7】:

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

              【讨论】:

                猜你喜欢
                • 2017-11-30
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多