Как дублировать элементы XML - PullRequest
7 голосов
/ 12 июля 2011

Мне нужно продублировать полезную нагрузку 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>

это возможно


Обновление: Приведенный ниже ответ работает нормально, но есть небольшая загвоздка, которую я не упомянул. Если идентификатор пользователя тот же, и он повторяется, то должна отображаться та же полезная нагрузка 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-код ---

</p>

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

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

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

<p>

Пожалуйста, подтвердите это. это тоже работает для меня ...

Ответы [ 7 ]

4 голосов
/ 12 июля 2011

Предполагаемый 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 голос
/ 13 июля 2011

Это преобразование (короткое, только два шаблона, без 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()

0 голосов
/ 23 июня 2018

На XSLT 1.0 есть еще две компактные версии с одинаковым результатом.

  1. На основе идентификатора генерации ()

    <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>
    
0 голосов
/ 06 апреля 2013

IN В дополнение к этому в XSLT 2.0 это просто Вместо написания нескольких шаблонов с apply-шаблонами, посмотрите код ниже с одним шаблоном, это даст тот же результат.

    <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>
0 голосов
/ 13 июля 2011

Следующая таблица стилей обрабатывает дубликаты:

<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>
0 голосов
/ 13 июля 2011

Одним из способов достижения желаемого результата является использование Identity Transformation и переопределение узла ns2:Details.

В шаблоне переопределения вы можете использовать команду повторения xsl:for-each, чтобы выполнить итерацию по всем UserId.

Для управления дубликатами UserId вы можете использовать хорошо известный предикат, полученный из метода группировки Менухи.

Поскольку мы будем использовать преобразование идентичности, способ генерации всего будет намного проще.

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

0 голосов
/ 12 июля 2011

Да, это возможно. Вы можете выполнить цикл с помощью для каждого цикла , используя для каждого узла ns2: UserID.

...