1
votes

I want to remove the element (the wrapper - not it descendants) called "searchBookResponse" from an XML file using XSLT.

When the "searchBookResponse" element has no attributes or non "xmlns" attributes, my XSLT code works just fine. However, with these xmlns attributes, it is not working anymore.

This is the input XML document:

<?xml version="1.0" encoding="utf-8"?>
<books>
  <searchBookResponse xmlns="urn:example.com" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <book>
      <author>Tom</author>
      <title>Test</title>
    </book>
    <book>
      <author>David</author>
      <title>Test two</title>
    </book>
  </searchBookResponse>
  <book>
    <author>Anas</author>
    <title>Test two</title>
  </book>
  <book>
    <author>Jim</author>
    <title>Test two</title>
  </book>
</books>

And this is the XSL Stylesheet:

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="@xmlns" />


  <xsl:template match="searchBookResponse">
      <xsl:apply-templates select="node()" />
  </xsl:template>
</xsl:stylesheet>

When applied, the output XML file is just the same as the input XML file. Must I apply some string pattern matching for removing the xmlns attribute first and then removing the actual element? I want to achieve something like this:

<?xml version="1.0" encoding="utf-8"?>
    <books>
        <book>
          <author>Tom</author>
          <title>Test</title>
        </book>
        <book>
          <author>David</author>
          <title>Test two</title>
        </book>
      <book>
        <author>Anas</author>
        <title>Test two</title>
      </book>
      <book>
        <author>Jim</author>
        <title>Test two</title>
      </book>
    </books>

-----------------------------------------------------------------

Ok, Tim and Michael provided me with a correct answer. But now I have extended the orginal document as follows:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
    <LibrarySearchResponse xmlns="urn:soft.librarysearch" xmlns:tns="urn:soft.librarysearch">
      <tns:query/>
      <tns:books>
        <searchBooksResponse xmlns="urn:soft.vub.ac.be/" xmlns:xsd="http://www.w3.org/2001/
XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
          <tns:book>
            <tns:author>Robert Ludlum</tns:author>
            <tns:isbn>95248457</tns:isbn>
            <tns:publicationDate>2004-01-01T00:00:00Z</tns:publicationDate>
            <tns:publisher>Orion</tns:publisher>
            <tns:title>The Bourne Identity</tns:title>
          </tns:book>
          <tns:book>
            <tns:author>Douglas Adams</tns:author>
            <tns:isbn>345453743</tns:isbn>
            <tns:publicationDate>2002-01-01T00:00:00Z</tns:publicationDate>
            <tns:publisher>Del Rey</tns:publisher>
            <tns:title>The Ultimate Hitchhiker's Guide to the Galaxy</tns:title>
          </tns:book>
          <tns:book>
            <tns:author>William Sleator</tns:author>
            <tns:isbn>810993562</tns:isbn>
            <tns:publicationDate>2008-01-01T00:00:00Z</tns:publicationDate>
            <tns:publisher>Amulet Books</tns:publisher>
            <tns:title>Test</tns:title>
          </tns:book>
          <tns:book>
            <tns:author>Jordan Belfort</tns:author>
            <tns:isbn>606259090</tns:isbn>
            <tns:publicationDate>2008-01-01T00:00:00Z</tns:publicationDate>
            <tns:publisher>Hodder Paperback</tns:publisher>
            <tns:title>The Wolf of Wall Street</tns:title>
          </tns:book>
        </searchBooksResponse>
        <tns:book xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://library.be"
        xmlns:nl="http://library.be" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
          <tns:author>James, E. L.</tns:author>
          <tns:isbn>0345803485</tns:isbn>
          <tns:publicationDate>3914-01-10T23:00:00.000Z</tns:publicationDate>
          <tns:publisher>Vintage Books</tns:publisher>
          <tns:title>50 Shades of Grey</tns:title>
        </tns:book>
        <tns:book xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://library.be"
        xmlns:nl="http://library.be" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
          <tns:author>James Dashner</tns:author>
          <tns:isbn>0385388896</tns:isbn>
          <tns:publicationDate>3914-04-20T22:00:00.000Z</tns:publicationDate>
          <tns:publisher>The Maze Runner Series</tns:publisher>
          <tns:title>Test</tns:title>
        </tns:book>
        <tns:book xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://library.be"
        xmlns:nl="http://library.be" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
          <tns:author>William Sleator</tns:author>
          <tns:isbn>0810993562</tns:isbn>
          <tns:publicationDate>3908-03-31T22:00:00.000Z</tns:publicationDate>
          <tns:publisher>Amulet Books</tns:publisher>
          <tns:title>Test</tns:title>
        </tns:book>
        <tns:book xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://library.be"
        xmlns:nl="http://library.be" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
          <tns:author>Douglas Adams</tns:author>
          <tns:isbn>0345453743</tns:isbn>
          <tns:publicationDate>3902-05-29T22:00:00.000Z</tns:publicationDate>
          <tns:publisher>Del Rey</tns:publisher>
          <tns:title>The Ultimate Hitchhiker's Guide to the Galaxy</tns:title>
        </tns:book>
        <tns:book xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://library.be"
        xmlns:nl="http://library.be" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
          <tns:author>Michalewicz, Zbigniew Fogel, David B.</tns:author>
          <tns:isbn>9783642061349</tns:isbn>
          <tns:publicationDate>3912-10-31T23:00:00.000Z</tns:publicationDate>
          <tns:publisher>Del Rey</tns:publisher>
          <tns:title>How to Solve It: Modern Heuristics</tns:title>
        </tns:book>
      </tns:books>
    </LibrarySearchResponse>
  </soapenv:Body>
</soapenv:Envelope>

I still wanted to remove the searchBookResponse element (the wrapper), but I get the following error: "The source document is in namespace http://schemas.xmlsoap.org/soap/envelope/, but none of the template rules match elements in this namespace."

However, when I shorten the document like this:

<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
    <LibrarySearchResponse xmlns="urn:soft.librarysearch" xmlns:tns="urn:soft.librarysearch">
      <books>
        <searchBookResponse xmlns="urn:soft.vub.ac.be" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
          <book>
            <author>Tom</author>
            <title>Test one</title>
          </book>
          <book>
            <author>David</author>
            <title>Test two</title>
          </book>
        </searchBookResponse>
        <book xmlns="http://library.be" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:nl="http://library.be" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
          <author>James, E. L.</author>
          <title>50 Shades of Grey</title>
        </book>
      </books>
    </LibrarySearchResponse>
  </soapenv:Body>
</soapenv:Envelope>

The searchBookResponse element - the wrapper, is removed, no error messages are displayed and the output is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Body>
      <LibrarySearchResponse xmlns="urn:soft.librarysearch" xmlns:tns="urn:soft.librarysearch">
         <books>
            <book xmlns="">
               <author>Tom</author>
               <title>Test one</title>
            </book>
            <book xmlns="">
               <author>David</author>
               <title>Test two</title>
            </book>
            <book xmlns="http://library.be"
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                  xmlns:nl="http://library.be"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
               <author>James, E. L.</author>
               <title>50 Shades of Grey</title>
            </book>
         </books>
      </LibrarySearchResponse>
   </soapenv:Body>
</soapenv:Envelope>

Using this stylesheet:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns1="urn:soft.vub.ac.be">

  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:strip-space elements="*"/>

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

  <xsl:template match="ns1:searchBookResponse" >
    <xsl:apply-templates select="*" mode="remove-namespace"/>
  </xsl:template>

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

</xsl:stylesheet>

Must I declare more namespaces and match each one of them? How comes that the shortened version works fine and the original, full version doesn't?

Thanks in advance!

2
Could you post the exact result you want to get? "I want to remove the element called "searchBookResponse"" can be read in two ways: either remove it completely, including all its descendants, or remove just the wrapper. - michael.hor257k

2 Answers

5
votes

The xmlns is not an attribute. It is a namespace declaration. In your case, it is the default namespace. It means the searchBookResponse element, and all nodes below it, belong to that namespace, and such elements are different to elements with the same name that are in different namespaces, or no namespace at all.

To solve your problem, you need to declare the namespace in your XSLT, using a prefix, and then use that prefix in identifying the element.

Try this XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  xmlns:e="urn:example.com" exclude-result-prefixes="e">
  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="e:searchBookResponse">
      <xsl:apply-templates select="node()" />
  </xsl:template>
</xsl:stylesheet>

Do note how the two book elements are also part of the namespace in the original XML, and so will also be part of the namespace in the output too. If you wanted to remove all traces of the namespace from the output XML, try adding this template to the XSLT too

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

If you want to read up on namespaces, try this article as a starter: http://www.xml.com/pub/a/1999/01/namespaces.html. It is really worth reading up on them, as they do catch out many people when they first encounter then.

3
votes

If you want to remove the <searchBookResponse xmlns="urn:example.com"> wrapper and add the books contained within to the rest of the books, you must also remove the namespace from those books.

Otherwise you will end up with two kinds of books: <book> and <book xmlns="urn:example.com">. These two are not the same thing.

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns1="urn:example.com">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

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

<xsl:template match="ns1:searchBookResponse" >
    <xsl:apply-templates select="*" mode="remove-namespace"/>
</xsl:template>

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

</xsl:stylesheet>

Result:

<?xml version="1.0" encoding="UTF-8"?>
<books>
   <book>
      <author>Tom</author>
      <title>Test</title>
   </book>
   <book>
      <author>David</author>
      <title>Test two</title>
   </book>
   <book>
      <author>Anas</author>
      <title>Test two</title>
   </book>
   <book>
      <author>Jim</author>
      <title>Test two</title>
   </book>
</books>