2011-02-25 27 views
5

tengo xml que se structed como seguirXML jerárquico a XML mediante XSLT plana

<root> 
    <PNode> 
    <node1> 
     <node1Child>data</node1Child> 
     <node2Child>data</node2Child> 
    </node1> 
    </PNode> 
    <SecondNode> 
    <node1> 
     <node1Child> 
     <child>data</child> 
     </node1Child> 
    </node1> 
    </SecondNode> 
</root> 

Quiero salida utilizando XSLT genric porque tengo muchos xml para convertir a este formato.

<root> 
    <Pnode-node1-node1Child>data</Pnode-node1-node1Child> 
    <Pnode-node1-node2Child>data</Pnode-node1-node2Child> 
    <SecondNode-node1-node1child-child>data</SecondNode-node1-node1child-child> 
</root> 

Podría ser más profundo o menos.
puedo hacerlo mediante XSLT pls da ningún ejemplo o referencia

quiero hacerlo para generar PDF desde el servidor SQL RDL 2k8 R2. porque rdl no acepta xml anidado, por lo que es necesario aplanarlo.

+0

presumir que quiere decir datos JasonPlutext

+0

Buena pregunta, 1. Vea mi respuesta para una solución completa, breve y fácil. También se proporciona una explicación extensa. La transformación es robusta y funcionará en caso de espacios de nombres y nombres prefijados. –

Respuesta

3

Esta hoja de estilo:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:strip-space elements="*"/> 
    <xsl:template match="/*"> 
     <xsl:copy> 
      <xsl:apply-templates/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="*"> 
     <xsl:param name="pName"/> 
     <xsl:apply-templates> 
      <xsl:with-param name="pName" select="concat($pName,name(),'-')"/> 
     </xsl:apply-templates> 
    </xsl:template> 
    <xsl:template match="text()"> 
     <xsl:param name="pName"/> 
     <xsl:element name="{substring($pName,1,string-length($pName)-1)}"> 
      <xsl:value-of select="."/> 
     </xsl:element> 
    </xsl:template> 
</xsl:stylesheet> 

de salida:

<root> 
    <PNode-node1-node1Child>data</PNode-node1-node1Child> 
    <PNode-node1-node2Child>data</PNode-node1-node2Child> 
    <SecondNode-node1-node1Child-child>data</SecondNode-node1-node1Child-child> 
</root> 

actualización: si pudiera haber nodos de vaciar ...

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:strip-space elements="*"/> 
    <xsl:template match="/*"> 
     <xsl:copy> 
      <xsl:apply-templates/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="*"> 
     <xsl:param name="pName"/> 
     <xsl:apply-templates> 
      <xsl:with-param name="pName" select="concat($pName,name(),'-')"/> 
     </xsl:apply-templates> 
    </xsl:template> 
    <xsl:template match="*[not(*)]"> 
     <xsl:param name="pName"/> 
     <xsl:element name="{$pName}{name()}"> 
      <xsl:value-of select="."/> 
     </xsl:element> 
    </xsl:template> 
</xsl:stylesheet> 

Nota: juego elemento más interno.

+0

+1 Mucho mejor que el mío. –

+0

+1 para una solución correcta. – Flack

+0

@Alejandro Respuesta pequeña y perfecta. Gracias una tonelada. – Techmaster

0

En su plantilla, pruebe un nodo secundario.

Si hay un nodo secundario, páselo valor del parámetro anterior concatenado con el nombre de este elemento.

Si sólo hay #text, la salida de un nuevo elemento utilizando el parámetro como su nombre, y establecer su contenido a la #text

+0

@Alex Nikolaenkov esto no me está dando nodos. Datadatadata de salida – Techmaster

+0

@Jagan, si utiliza el navegador para ver el resultado, use 'Ver fuente' para verificar el documento xml. Verifiqué mi hoja de estilo con el procesador Xalan XSLT. –

1

Dada esta entrada:

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <PNode> 
     <node1> 
      <node1Child>data</node1Child> 
      <node2Child>data</node2Child> 
     </node1> 
    </PNode> 
    <SecondNode> 
     <node1> 
      <node1Child> 
       <child>data</child> 
      </node1Child> 
     </node1> 
    </SecondNode> 
</root> 

La siguiente hoja de estilo:

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

    <xsl:strip-space elements="*"/> 
    <xsl:output indent="yes" method="xml"/> 

    <xsl:template match="root"> 
     <xsl:copy> 
      <xsl:apply-templates select="*" mode="flatten"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="*[normalize-space(text())]" mode="flatten"> 
     <xsl:param name="name-prefix" select="''"/> 
     <xsl:variable name="name"> 
      <xsl:call-template name="construct-name"> 
       <xsl:with-param name="name-prefix" select="$name-prefix"/> 
      </xsl:call-template> 
     </xsl:variable> 

     <xsl:element name="{$name}"> 
      <xsl:apply-templates select="text()"/> 
     </xsl:element> 

     <xsl:apply-templates select="node()" mode="flatten"> 
      <xsl:with-param name="name-prefix" select="$name"/> 
     </xsl:apply-templates> 
    </xsl:template> 

    <xsl:template match="*[not(normalize-space(text()))]" mode="flatten"> 
     <xsl:param name="name-prefix" select="''"/> 

     <xsl:variable name="prefix"> 
      <xsl:call-template name="construct-name"> 
       <xsl:with-param name="name-prefix" select="$name-prefix"/> 
      </xsl:call-template> 
     </xsl:variable> 

     <xsl:apply-templates select="node()" mode="flatten"> 
      <xsl:with-param name="name-prefix" select="$prefix"/> 
     </xsl:apply-templates> 
    </xsl:template> 

    <xsl:template name="construct-name"> 
     <xsl:param name="name-prefix"/> 
     <xsl:choose> 
      <xsl:when test="$name-prefix"> 
       <xsl:value-of select="concat($name-prefix, '-', local-name(.))"/> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:value-of select="local-name(.)"/> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 

    <xsl:template match="text()" mode="flatten"/> 
</xsl:stylesheet> 

Produce resultado deseado:

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <PNode-node1-node1Child>data</PNode-node1-node1Child> 
    <PNode-node1-node2Child>data</PNode-node1-node2Child> 
    <SecondNode-node1-node1Child-child>data</SecondNode-node1-node1Child-child> 
</root> 
+0

Oye alex no funciona, probé en xml spy, stylus stuio y vía código C#. solo da nodos de datos que no llegan. Acabo de obtener datadatadata. Pls vuelva a verificarlo. – Techmaster

+0

@Techmaster, especifique su procesador 'XSLT' por favor. –

+0

Lo estoy procesando usando mi código en C#. También probé con la herramienta XML Spy. Todos están dando solo datos. Gracias. – Techmaster

3

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:template match="/*"> 
    <root> 
     <xsl:apply-templates/> 
    </root> 
</xsl:template> 

<xsl:template match="text()"> 
    <xsl:variable name="vCompName"> 
    <xsl:for-each select="ancestor::*[not(position() =last())]"> 
    <xsl:value-of select="translate(name(), ':', '_')"/> 
    <xsl:if test="not(position()=last())">-</xsl:if> 
    </xsl:for-each> 
    </xsl:variable> 

    <xsl:element name="{$vCompName}"> 
    <xsl:value-of select="."/> 
    </xsl:element> 
</xsl:template> 
</xsl:stylesheet> 

cuando se aplica en el documento XML proporcionado:

<root> 
    <PNode> 
     <node1> 
      <node1Child>data</node1Child> 
      <node2Child>data</node2Child> 
     </node1> 
    </PNode> 
    <SecondNode> 
     <node1> 
      <node1Child> 
       <child>data</child> 
      </node1Child> 
     </node1> 
    </SecondNode> 
</root> 

produce el, resultado correcto deseada:

<root> 
    <PNode-node1-node1Child>data</PNode-node1-node1Child> 
    <PNode-node1-node2Child>data</PNode-node1-node2Child> 
    <SecondNode-node1-node1Child-child>data</SecondNode-node1-node1Child-child> 
</root> 

Explicación:

  1. Además envolviendo el documento en un elemento superior root, sólo hay una única plantilla. Coincide con cualquier nodo de texto que no sea de espacio en blanco.

  2. El conjunto de nodos de todos los elementos antecesores excepto el primero en orden de documento (que es el último en el eje inverso ancestor::) son cuerdas empalmadas con un carácter '-' y un elemento se construye con esta cadena como un nombre.

  3. Antes de la operación de unión de cadena en 2. anterior, cada nombre se modifica para que cualquier carácter ':' en él se sustituya por un carácter de subrayado. Esto hace que la transformación no produzca nombres compuestos inválidos si hay prefijos de espacio de nombres en algunos nombres.

  4. Finalmente, el nodo de texto actual se copia como elemento secundario del elemento construido dinámicamente.

+0

+1 para una buena respuesta. Por cierto, ¿por qué su exprés 'if test' está envuelto en un paréntesis extra? – Flack

+0

@Flack: Gracias. En cuanto al paréntesis adicional: comencé a escribir esto como una declaración C# 'if', luego noté lo que estaba haciendo y lo puse dentro de un atributo' test' :). Lo simplificará –