Remove parent nodes from xml with xslt

2019-08-07 14:53发布

I need to transform one XML file to another XML file removing some parent nodes.

INPTUT Xml:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:autorizacionComprobanteResponse xmlns:ns2="http://ec.gob.sri.ws.autorizacion">
<RespuestaAutorizacionComprobante>
  <claveAccesoConsultada>2</claveAccesoConsultada>
  <numeroComprobantes>1</numeroComprobantes>
  <autorizaciones>
    <autorizacion>
      <estado>AUTORIZADO</estado>
      <numeroAutorizacion>2</numeroAutorizacion>
      <fechaAutorizacion>2015-05-21T14:22:30.764-05:00</fechaAutorizacion>
      <ambiente>PRUEBAS</ambiente>
      <comprobante>
        <factura id="comprobante" version="1.0.0">
          <infoTributaria>
            <ambiente>1</ambiente><tipoEmision>1</tipoEmision>
          </infoTributaria>
        </factura>
      </comprobante>
      <mensajes>
        <mensaje>
          <identificador>60</identificador>
        </mensaje>
      </mensajes>
    </autorizacion>
  </autorizaciones>
</RespuestaAutorizacionComprobante>
</ns2:autorizacionComprobanteResponse>
</soap:Body>
</soap:Envelope>

In the xml output file I only need the node "<autorizacion>" without the "<mensajes>" subnode like this:

Desired xml output:

<autorizacion>
    <estado>AUTORIZADO</estado>
    <numeroAutorizacion>2</numeroAutorizacion>
    <fechaAutorizacion>2015-05-21T14:22:30.764-05:00</fechaAutorizacion>
    <ambiente>PRUEBAS</ambiente>
    <comprobante>
        <factura id="comprobante" version="1.0.0">
            <infoTributaria><ambiente>1</ambiente><tipoEmision>1</tipoEmision>
        </factura>
    </comprobante>
</autorizacion>

I'm new to xslt but I have tried several examples and this code gets the closest output:

Xsl file:

<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="autorizacion">
        <xsl:copy-of select="." />
    </xsl:template>
    <xsl:template match="mensajes"/>
</xsl:stylesheet>

This is the xml file I get with that xsl:

2
1
    <autorizacion xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns2="http://ec.gob.sri.ws.autorizacion">
        <estado>AUTORIZADO</estado>
        <numeroAutorizacion>2</numeroAutorizacion>
        <fechaAutorizacion>2015-05-21T14:22:30.764-05:00</fechaAutorizacion>
        <ambiente>PRUEBAS</ambiente>
        <comprobante>
            <factura id="comprobante" version="1.0.0">
                <infoTributaria><ambiente>1</ambiente><tipoEmision>1</tipoEmision>
            </factura>
        </comprobante>
        <mensajes>
            <mensaje>
                <identificador>60</identificador>
            </mensaje>
        </mensajes>
    </autorizacion>

I don't know why the xmlns:soap and xmlns:ns2 tags are added to the node <autorizacion> and also the <mensajes> node is still present. Please help me to solve this, I also need to remove the empty lines but keeping the indentation if possible.

标签: xml xslt
3条回答
forever°为你锁心
2楼-- · 2019-08-07 15:41

I see this a slight variation on the identity transform:

<?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:strip-space elements="*"/>

  <xsl:template match="/">
    <xsl:apply-templates select="//autorizacion"/>
  </xsl:template>

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

  <xsl:template match="mensajes"/>
</xsl:stylesheet>

I don't know how this stacks up against other answers here, which all look identity-transform-ish. Here's the output I get:

<autorizacion>
  <estado>AUTORIZADO</estado>
  <numeroAutorizacion>2</numeroAutorizacion>
  <fechaAutorizacion>2015-05-21T14:22:30.764-05:00</fechaAutorizacion>
  <ambiente>PRUEBAS</ambiente>
  <comprobante>
    <factura id="comprobante" version="1.0.0">
      <infoTributaria>
        <ambiente>1</ambiente><tipoEmision>1</tipoEmision>
      </infoTributaria>
    </factura>
  </comprobante>    
</autorizacion>

Update 1

I think that matthias_h's answer and mine are by-and-large the same.

<xsl:template match="*">
  <xsl:element name="{local-name()}">
    <xsl:apply-templates select="node()|@*"/>
  </xsl:element>
</xsl:template>
<xsl:template match="@*">
  <xsl:attribute name="{local-name(.)}">
    <xsl:value-of select="."/>
  </xsl:attribute>
</xsl:template>

is practically equivalent to:

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

which is practically equivalent to:

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

matthias_h's solution is much more explicit, using "step-by-step" directives, and allows more finesse if needed.

查看更多
Explosion°爆炸
3楼-- · 2019-08-07 15:47

For removing of the namespaces when copying you just need to say copy-namespaces='no'. Also, you might want to consider using strip-space to remove all the white spaces generated by the transformation. And finally, you just need to mention which node you need to exclude.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:ns2="http://ec.gob.sri.ws.autorizacion"
   xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">

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


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


<xsl:template match="@*|node()[not(self::mensajes) and (ancestor::autorizacion)]">
    <xsl:copy copy-namespaces='no'>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>


 <xsl:template match="mensajes|claveAccesoConsultada|numeroComprobantes"/>

 </xsl:stylesheet>

output is:

<autorizacion>
   <estado>AUTORIZADO</estado>
   <numeroAutorizacion>2</numeroAutorizacion>
   <fechaAutorizacion>2015-05-21T14:22:30.764-05:00</fechaAutorizacion>
   <ambiente>PRUEBAS</ambiente>
   <comprobante>
      <factura id="comprobante" version="1.0.0">
         <infoTributaria>
            <ambiente>1</ambiente>
            <tipoEmision>1</tipoEmision>
         </infoTributaria>
      </factura>
   </comprobante>
</autorizacion>
查看更多
成全新的幸福
4楼-- · 2019-08-07 15:51

The mensajes node ist still present because you copy the autorizacion node. When applying templates to the content of autorizacion, the empty template matching mensajes will remove this element.
The namespaces of the parent nodes of autorizacion are added to the ouput. To remove the namespaces, it's e.g. possible to write the autorizacion and following child nodes without the namespace like this:

<xsl:template match="*">
  <xsl:element name="{local-name(.)}">
    <xsl:apply-templates select="@* | node()"/>
  </xsl:element>
</xsl:template>

This template matches any node, creates an element with the name of the currently matched node and applies templates to all attributes and child nodes, without adding any namespace.
To remove empy lines and whitespace, you can use <xsl:strip-space elements="*"/>

The following XSLT

<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="/" >
    <xsl:apply-templates select="//autorizacion" />
  </xsl:template>
  <xsl:template match="*">
    <xsl:element name="{local-name()}">
      <xsl:apply-templates select="node()|@*"/>
    </xsl:element>
  </xsl:template>
  <xsl:template match="@*">
    <xsl:attribute name="{local-name(.)}">
      <xsl:value-of select="."/>
    </xsl:attribute>
  </xsl:template>
  <xsl:template match="mensajes"/>
</xsl:stylesheet>

when applied to your input XML produces the output

<autorizacion>
  <estado>AUTORIZADO</estado>
  <numeroAutorizacion>2</numeroAutorizacion>
  <fechaAutorizacion>2015-05-21T14:22:30.764-05:00</fechaAutorizacion>
  <ambiente>PRUEBAS</ambiente>
  <comprobante>
  <factura id="comprobante" version="1.0.0">
     <infoTributaria>
        <ambiente>1</ambiente>
        <tipoEmision>1</tipoEmision>
     </infoTributaria>
    </factura>
  </comprobante>
</autorizacion>
查看更多
登录 后发表回答