Transformar para eliminar duplicados y copiar el resto

Quiero que el xml de salida se haya agrupado para el elemento 'c', según el atributo 'f'. Aquí está mi entrada xml y xslt. Quiero que el grupo ocurra solo una vez y los otros nodos se deben copiar tal como están en la salida. El xslt que probé, copia todo el xml de entrada. Entonces, si hay dos o más elementos con el elemento c y el mismo valor de atributo para 'f', querrá la primera aparición de ese grupo en la salida. Mi resultado deseado también se copia.

entrada xml

<M>
   <a>
      <b>
         <c f="123">
            <d>Al</d>
            <e NO="678">
               <f>Y</f>
               <g>
                  <h>FTO</h>
               </g>
            </e>
         </c>
      </b>
   </a>
  <a>
    <b>
      <c f="123">
        <d>Al</d>
        <e NO="678">
          <f>Y</f>
          <g>
            <h>FTO</h>
          </g>
        </e>
      </c>
    </b>
  </a>
  <a>
    <b>
      <c f="567">
        <d>Al</d>
        <e NO="678">
          <f>Y</f>
          <g>
            <h>FTO</h>
          </g>
        </e>
      </c>
    </b>
  </a>
  <a>
    <b>
      <somethingelse></somethingelse>
    </b>
  </a>
</M>

salida deseada xml

<M>
  <a>
    <b>
      <c f="123">
        <d>Al</d>
        <e NO="678">
          <f>Y</f>
          <g>
            <h>FTO</h>
          </g>
        </e>
      </c>
    </b>
  </a>
  <a>
    <b>
      <c f="567">
        <d>Al</d>
        <e NO="678">
          <f>Y</f>
          <g>
            <h>FTO</h>
          </g>
        </e>
      </c>
    </b>
  </a>
  <a>
    <b>
      <somethingelse></somethingelse>
    </b>
  </a>
</M>

xslt lo intenté

<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="mykey" match="c"
   use="@f"/>

  <xsl:template match=
  "c[generate-id()
      =
       generate-id(key('mykey',@f)[1])
      ]
  ">



    <xsl:text/>
    <xsl:copy-of select="key('mykey',@f)[1]"/>
  </xsl:template>
  <xsl:template match="node()|@*">
        <xsl:copy>
          <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

preguntado el 12 de junio de 12 a las 17:06

4 Respuestas

Esta transformación:

<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="kAByC-F" match="a" use="*/c/@f"/>

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

     <xsl:template match=
      "a[*/c
       and
         not(generate-id()
            =
             generate-id(key('kAByC-F', */c/@f)[1])
             )
        ]"/>
</xsl:stylesheet>

cuando se aplica en el documento XML proporcionado:

<M>
   <a>
      <b>
         <c f="123">
            <d>Al</d>
            <e NO="678">
               <f>Y</f>
               <g>
                  <h>FTO</h>
               </g>
            </e>
         </c>
      </b>
   </a>
  <a>
    <b>
      <c f="123">
        <d>Al</d>
        <e NO="678">
          <f>Y</f>
          <g>
            <h>FTO</h>
          </g>
        </e>
      </c>
    </b>
  </a>
  <a>
    <b>
      <c f="567">
        <d>Al</d>
        <e NO="678">
          <f>Y</f>
          <g>
            <h>FTO</h>
          </g>
        </e>
      </c>
    </b>
  </a>
  <a>
    <b>
      <somethingelse></somethingelse>
    </b>
  </a>
</M>

produce el resultado deseado y correcto:

<M>
   <a>
      <b>
         <c f="123">
            <d>Al</d>
            <e NO="678">
               <f>Y</f>
               <g>
                  <h>FTO</h>
               </g>
            </e>
         </c>
      </b>
   </a>
   <a>
      <b>
         <c f="567">
            <d>Al</d>
            <e NO="678">
               <f>Y</f>
               <g>
                  <h>FTO</h>
               </g>
            </e>
         </c>
      </b>
   </a>
   <a>
      <b>
         <somethingelse/>
      </b>
   </a>
</M>

Explicación:

Uso adecuado del Método de agrupación muenchiano.

Respondido el 13 de junio de 12 a las 12:06

Una solución simple sería simplemente agregar una plantilla vacía para todos los siguientes c nodos:

<xsl:template match="c[generate-id() = generate-id(key('mykey',@f)[position() &gt; 1])]" />

Respondido el 12 de junio de 12 a las 18:06

Solo esto elimina el duplicado. c elementos -- sin embargo, la tarea es eliminar el subárbol completo, enraizado en el a antepasado de cada duplicado. Esto es lo que está haciendo la solución provista en mi respuesta. - dimitre novachev

Hmm... Tendré que pensar esto un poco más (todavía estoy desafiado por Muenchian Grouping, pero tengo muchas ganas de aprender). Lo curioso es que parece producir el resultado deseado. Por cierto: su salida (que se muestra) pierde la parte que contiene <somethingelse> (aún no probé tu xslt). - Filburt

Una idea podría ser guardar todos los valores de c en una variable en un formato que le permita diferenciarlos entre sí, y luego, cada vez que encuentre c, verifique si ese valor está contenido dentro de la variable. Si es así, salta al siguiente nodo. Si no es así, continúe procesando el nodo actual.

Dime si necesitas información más específica

EDITAR: como alternativa, y probablemente un método más fácil (he estado usando NAnt recientemente, por lo que podría estar brindándole una estrategia NAnt) es ordenar todos los nodos por sus valores s. Luego, solo tenga una variable que almacene el valor actual de c y compare hasta que el valor que está viendo no sea igual al valor almacenado. ¡Luego reasigna el valor y hazlo de nuevo!

Respondido el 12 de junio de 12 a las 18:06

Puedes emparejar <a> elementos y verifique si hay hermanos anteriores con el mismo f atributo en su <c> subelementos. Si los hay, ha encontrado un duplicado de un determinado f valor (una ocurrencia de un f valor que no es la primera aparición de ese valor) y simplemente puede anular la plantilla de identidad para omitir el elemento:

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

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

    <xsl:template match="/M/a[b/c/@f = preceding-sibling::a/b/c/@f]"/>

</xsl:stylesheet>

Una ventaja de esta solución es que no requiere ningún conocimiento sobre la generación de claves o ID; solo funciona con las funciones básicas del eje XPath. Sin embargo, puede complicarse un poco más cuando los elementos a comparar no están todos en la misma profundidad de anidamiento/en la misma jerarquía relativa de elementos.

PD: eliminé el <xsl:strip-space elements="*"/> elemento porque no pude probarlo (mi procesador Xml afirmó que solo puedo usarlo si paso un flujo legible en lugar de un archivo), pero no dude en volver a insertarlo si funciona para usted.

Respondido el 13 de junio de 12 a las 07:06

¿No podrías reemplazar toda la segunda plantilla con <xsl:template match="/M/a[b/c/@f = preceding-sibling::a/b/c/@f]"/>? - daniel haley

@DevNull: Posiblemente. Creo que encontré y leí algo sobre las expresiones XPath en la plantilla match Sin embargo, los atributos no admiten algunos tipos de restricciones. Ya no puedo encontrarlo, pero entonces me dio dolor de cabeza :-/- OR Mapper

@DevNull: Tienes razón; lo que estaba recordando tenía que ver exclusivamente con el uso de valores de parámetros. He simplificado el código en mi respuesta. Gracias. - OR Mapper

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.