Pregunta

Deseo traducir un archivo XML con datos como el siguiente:

<FlatData>
    <Details1_Collection>
        <Details1 Customer1="Customer" Total1="3" />
        ...
    </Details1_Collection>
</FlatData>

Los datos que me interesa es los atributos y sus valores en cada Details1. El problema es que estos atributos no necesariamente van a ser los mismos en todos los archivos XML que quiero traducir, y quiero un XSL propósito general que pudiera manejar tales Details1 ya que:

<Details1 Customer1="Customer" Total1="3" />
<Details1 Name="Jim" Age="14" Weight="180" />
<Details1 Date="2009-07-27" Range="1-5" Option1="True" />

Estos diferentes Details1 no ocurriría en el mismo archivo XML de origen, sino en diferentes archivos. Sin embargo, me gustaría utilizar el mismo XSL en cada uno.

Yo estaba pensando que necesitaba algo así como <xsl:value-of select="@attribute_name"/> pero lo que puedo poner para @attribute_name cuando no sé de antemano qué atributos habrá? Además, ¿cómo puedo capturar el nombre del atributo? Me gustaría hacer explotar la fuente XML arriba para algo como:

<Details1>
    <Customer1>Customer</Customer1>
    <Total1>3</Total1>
</Details1>

Editar: gracias por las respuestas! Estoy teniendo problemas para conseguir más de la siguiente salida, sin embargo:

<?xml version="1.0" encoding="UTF-8"?>
<FlatData>
<Details1_Collection></Details1_Collection>
</FlatData>

He intentado tanto de Lavinio y respuestas de Jörn Horstmann, así como tratar de combinar los dos. Corro este comando:

msxsl.exe -o output.xml input.xml transform.xsl

Creo que algo que está en el camino es un espacio de nombres en el archivo de entrada :

<Report Name="MyReport" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="MyReport">
¿Fue útil?

Solución

Hubo un aumento de dificultad debido a la Microsoft SQL Reporting Services 2008 espacio de nombres que formó parte de la entrada XML. No me di cuenta en un principio que era una línea <Report Name="MyReport" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="MyReport"> tan importante. Gracias a Pavel Minaev para el espacio de nombres comentario . La siguiente XSL trabajó para extraer los datos que quería:

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

  <xsl:template match="/">
    <xsl:for-each select="a:Report/a:FlatData/a:Details1_Collection/a:Details1">
      <xsl:element name="{name(.)}">
        <xsl:for-each select="@*">
          <xsl:element name="{name(.)}">
            <xsl:value-of select="."/>
          </xsl:element>
        </xsl:for-each>
      </xsl:element>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

Creo que voy a tratar de limpiar esto para usar el estilo apply-templates que lavinio sugerido. Gracias también a Jörn Horstmann para el código select="@*" en bucles for-each . Sería interesante averiguar por qué los informes de Reporting Services se vierten inicialmente con el valor xmlns establecido en el nombre del informe, y no un URL esquema .

Voy a seguir para actualizar esta respuesta como refinar esta XSL.

Editar esta es una versión independiente del espacio de nombres ya que, para cada informe diferente de Reporting Services, hay al parecer habrá un espacio de nombres diferentes:

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

  <xsl:template match="/">
    <xsl:for-each select="*[local-name()='Report']/*[local-name()='FlatData']/*[local-name()='Details1_Collection']/*[local-name()='Details1']">
      <Details>
        <xsl:for-each select="@*">
          <xsl:element name="{name(.)}">
            <xsl:value-of select="."/>
          </xsl:element>
        </xsl:for-each>
      </Details>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

Otros consejos

Puede utilizar "@*" para referirse a todos los atributos, como estos ejemplos:

  • <xsl:value-of select="@*"/>
  • <xsl:apply-templates select="@*"/>
  • <xsl:template match="@*">

El constructo <xsl:element name=""> se puede utilizar para crear un nuevo elemento con un nombre arbitrario, y las funciones name() o local-name() devolverá el nombre de un atributo específico.

Para hacer lo que quiera, intenta algo a lo largo de estas líneas:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/">
        <FlatData>
            <Details1_Collection>
                <xsl:apply-templates select="FlatData/Details1_Collection/Details1"/>
            </Details1_Collection>
        </FlatData>
    </xsl:template>
    <xsl:template match="Details1">
        <Details1>
            <xsl:apply-templates select="@*"/>
        </Details1>
    </xsl:template>
    <xsl:template match="@*">
        <xsl:element name="{name()}">
            <xsl:value-of select="."/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

Para resolver el problema de espacio de nombres (para ambas respuestas), agregar una declaración de espacio con un prefijo a su XLST:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:r="MyReport"
                version="1.0">

y luego usarlo en todas sus expresiones XPath para calificar los elementos, por ejemplo:

<xsl:template match="//r:Details1">

¿Esta transformación da el resultado que quieres?

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:output method="xml" indent="yes" encoding="utf-8" />

    <xsl:template match="//Details1">
        <Details1>
            <xsl:for-each select="@*">
                <xsl:element name="{name(.)}"><xsl:value-of select="." /></xsl:element>
            </xsl:for-each>
        </Details1>
    </xsl:template>

</xsl:stylesheet>

otra manera de escribir la respuesta de Jörn Horstmann (si es que tiene que hacer esto con details1, Details2, y así sucesivamente ...) sería:

<xsl:template match="//Details1 | //Details2 | //whatever">
  <xsl:copy>
    <xsl:apply-templates select="@*"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="@*">
  <xsl:element name="{name(.)}">
    <xsl:value-of select="." />
  </xsl:element>
</xsl:template>

Probablemente la forma más sencilla de hacerlo:

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

  <xsl:template match="/">
    <FlatData>
      <xsl:copy-of select="//Details1" />
    </FlatData>
  </xsl:template>
</xsl:stylesheet>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top