2008-12-30 26 views
8

Dado el siguiente fragmento xml:elementos distintos y agrupar

<Problems> 
    <Problem> 
    <File>file1</File> 
    <Description>desc1</Description> 
    </Problem> 
    <Problem> 
    <File>file1</File> 
    <Description>desc2</Description> 
    </Problem> 
    <Problem> 
    <File>file2</File> 
    <Description>desc1</Description> 
    </Problem> 
</Problems> 

necesito para producir algo así como

<html> 
    <body> 
    <h1>file1</h1> 
    <p>des1</p> 
    <p>desc2</p> 
    <h1>file2</h1> 
    <p>des1</p> 
    </body> 
</html> 

He intentado utilizar una clave, al igual que

<xsl:key name="files" match="Problem" use="File"/> 

pero Don Realmente entiendo cómo llevarlo al siguiente paso, o si ese es el enfoque correcto.

Respuesta

5

Así es como lo haría, utilizando el método Muenchean. Google 'xslt muenchean' para obtener más información de personas más inteligentes. Puede haber una manera inteligente, pero se lo dejo a los demás.

Una nota, evito el uso de mayúsculas al comienzo de los nombres de elementos xml, por ejemplo, 'Archivo', pero eso depende de usted.

<?xml version="1.0"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="html"/> 
    <xsl:key name="files" match="/Problems/Problem/File" use="./text()"/> 
    <xsl:template match="/"> 
     <html> 
      <body> 
       <xsl:apply-templates select="Problems"/> 
      </body> 
     </html> 
    </xsl:template> 
    <xsl:template match="Problems"> 
     <xsl:for-each select="Problem/File[generate-id(.) = generate-id(key('files', .))]"> 
      <xsl:sort select="."/> 
      <h1> 
       <xsl:value-of select="."/> 
      </h1> 
      <xsl:apply-templates select="../../Problem[File=current()/text()]"/> 
     </xsl:for-each> 
    </xsl:template> 
    <xsl:template match="Problem"> 
     <p> 
      <xsl:value-of select="Description/text()"/> 
     </p> 
    </xsl:template> 
</xsl:stylesheet> 

La idea es, clave cada elemento del archivo usando su valor de texto. Luego, solo muestre los valores del archivo si son el mismo elemento que el que tiene la clave. Para verificar si son iguales, use generate-id. Hay un enfoque similar donde se compara el primer elemento que coincide. No puedo decirte cuál es más eficiente.

He probado el código aquí usando Marrowsoft Xselerator, mi herramienta xslt favorita, aunque ya no está disponible, afaik. El resultado que obtuve es:

<html> 
<body> 
<h1>file1</h1> 
<p>desc1</p> 
<p>desc2</p> 
<h1>file2</h1> 
<p>desc1</p> 
</body> 
</html> 

Esto está usando msxml4.

He ordenado la salida por Archivo. No estoy seguro si quieres eso.

Espero que esto ayude.

+0

Perfecto, gracias! Y aplausos para la punta muencheana ... – rjohnston

+0

Saludos. Ni siquiera me di cuenta de que eres un australiano, de lo contrario habría utilizado muchos más coloquialismos. :) –

+0

@rjonston: He proporcionado una solución un poco más limpia y probablemente un poco más eficiente que Richards. –

7

Esta solución es un poco más simple, más eficiente y al mismo tiempo más general que la presentada por Richard:

Esta transformación:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<!--           --> 
<xsl:key name="kFileByVal" match="File" 
     use="." /> 
<!--           --> 
<xsl:key name="kDescByFile" match="Description" 
     use="../File"/> 
<!--           --> 
    <xsl:template match="/*"> 
    <html> 
     <body> 
     <xsl:for-each select= 
     "*/File[generate-id() 
       = 
       generate-id(key('kFileByVal',.)[1])]"> 
     <h1><xsl:value-of select="."/></h1> 
     <xsl:for-each select="key('kDescByFile', .)"> 
      <p><xsl:value-of select="."/></p> 
     </xsl:for-each> 
     </xsl:for-each> 
     </body> 
    </html> 
    </xsl:template> 
</xsl:stylesheet> 

cuando se aplica a el documento XML proporcionado:

<Problems> 
    <Problem> 
     <File>file1</File> 
     <Description>desc1</Description> 
    </Problem> 
    <Problem> 
     <File>file1</File> 
     <Description>desc2</Description> 
    </Problem> 
    <Problem> 
     <File>file2</File> 
     <Description>desc1</Description> 
    </Problem> 
</Problems> 

Produce el resultado deseado:

<html> 
    <body> 
     <h1>file1</h1> 
     <p>desc1</p> 
     <p>desc2</p> 
     <h1>file2</h1> 
     <p>desc1</p> 
    </body> 
</html> 

hacer la nota el patrón de comparación sencilla de la primera <xsl:key> y cómo, utilizando un segundo <xsl:key>, localizamos todos "Description" elementos que son hermanos de un "File "elemento que tiene un valor dado.

Podríamos haber usado más plantillas en lugar de <xsl:for-each> extracción de procesamiento, sin embargo, este es un caso bastante simple y la solución realmente se beneficia de un código más corto, más compacto y más legible.

También tenga en cuenta, que en XSLT 2.0 uno se suelen utilizar la instrucción <xsl:for-each-group> en lugar de la Muenchian method.

+0

@Dimitre. Solo he usado llaves para la agrupación muencheana. Gracias por un ejemplo de usarlos de una manera más expansiva. Además, aún no he analizado XSLT 2.0, por lo que el grupo para cada uno también es interesante. Salud y feliz año nuevo. –

+0

@Richard: Feliz año nuevo para ti, Richard. –

0

Este XSLT 1.0 solución también hará el truco. ¡Un poco más sucinto que las otras soluciones!

<xsl:template match="/">   
    <html><body> 
     <xsl:for-each select="//File[not(.=preceding::*)]"> 
     <h1><xsl:value-of select="." /></h1> 
     <xsl:for-each select="//Problem[File=current()]/Description"> 
      <p><xsl:value-of select="." /></p> 
     </xsl:for-each> 
     </xsl:for-each> 
    </body></html> 
    </xsl:template> 

Resultado:

<html xmlns="http://www.w3.org/1999/xhtml"> 
    <body> 
    <h1>file1</h1> 
    <p>desc1</p> 
    <p>desc2</p> 
    <h1>file2</h1> 
    <p>desc1</p> 
    </body> 
</html> 
Cuestiones relacionadas