paulsm4
paulsm4

Reputation: 121849

XSLT: how can I filter out all elements with a specific namespace

I have an XML file that looks like this:

<zoo xmlns="http://myurl.com/wsdl/myservice">
  <animal>elephant</animal>
  <exhibit>
    <animal>walrus</animal>
    <animal>sea otter</animal>
    <trainer xmlns="http://example.com/abc">Jack</trainer>
  </exhibit>
  <exhibit xmlns="http://example.com/abc">
    <animal>lion</animal>
    <animal>tiger</animal>
  </exhibit>
</zoo>

How can I write an XSLT stylesheet that filters out all elements and/or all node trees in the "http://example.com/abc" namespace?

Desired result XML:

<zoo xmlns="http://myurl.com/wsdl/myservice">
  <animal>elephant</animal>
  <exhibit>
    <animal>walrus</animal>
    <animal>sea otter</animal>
  </exhibit>
</zoo>

I'm trying something like this, but it just isn't working:

Failed XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:a="http://example.com/abc"
    exclude-result-prefixes="a">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="@*|node()">
        <xsl:copy>
          <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Update

I got a good suggestion, but it's still not working. Here's the Java code I'm using to do the XSLT translation:

import java.io.*;
import org.w3c.dom.*; // XML DOM
import javax.xml.parsers.*; // DocumentBuilder, etc
import javax.xml.transform.*; // Transformer, etc
import javax.xml.transform.stream.*; // StreamResult, StreamSource, etc
import javax.xml.transform.dom.DOMSource;

public class Test {

    public static void main(String[] args) {
        new Test().testZoo();
    }

    public void testZoo () {
System.out.println ("Reading zoo.xml");     
        String zooXml = MassageXfddXml.readXmlFile ("zoo.xml");
        if (zooXml == null)
            return;
        
        try {
            // Read XFDD input string into DOM
            DocumentBuilder docBuilder =
                DocumentBuilderFactory.newInstance().newDocumentBuilder ();
            Document xfddDoc = 
                docBuilder.parse(new StringBufferInputStream (zooXml));
        
            // Filter out all elements in "http://example.com/abc" namespace
            StreamSource styleSource = new StreamSource (new File ("zoo.xsl"));
            Transformer transformer =
                TransformerFactory.newInstance().newTransformer (styleSource);
        
            // Convert final DOM back to String
            StringWriter buffer = new StringWriter ();
            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,  "yes"); // Remember: we want to insert this XML as a subnode
            transformer.setOutputProperty(OutputKeys.INDENT,  "yes");
            transformer.transform(new DOMSource(xfddDoc), new StreamResult (buffer));
            String translatedXml = buffer.toString();
System.out.println ("initial XML:\n" + zooXml + "\nfinal XML:\n" + translatedXml);          
        }
        catch (Exception e) {
            System.out.println ("convertTransactionData error: " + e.getMessage());
            e.printStackTrace ();
        }
    }

Here's the new stylesheet:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:a="http://example.com/abc"
    exclude-result-prefixes="a"
>
    <xsl:output method="xml" indent="yes"/>

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

    <xsl:template match="a:* | @a:*" />
</xsl:stylesheet>

And here's the output:

Reading zoo.xml
bytes read=337
initial XML:
<zoo xmlns="http://myurl.com/wsdl/myservice">
  <animal>elephant</animal>
  <exhibit>
    <animal>walrus</animal>
    <animal>sea otter</animal>
    <trainer xmlns="http://example.com/abc">Jack</trainer>
  </exhibit>
  <exhibit xmlns="http://example.com/abc">
    <animal>lion</animal>
    <animal>tiger</animal>
  </exhibit>
</zoo>

final XML:
<zoo xmlns="http://myurl.com/wsdl/myservice">
  <animal>elephant</animal>
  <exhibit>
    <animal>walrus</animal>
    <animal>sea otter</animal>
    <trainer xmlns="http://example.com/abc">Jack</trainer>
  </exhibit>
  <exhibit xmlns="http://example.com/abc">
    <animal>lion</animal>
    <animal>tiger</animal>
  </exhibit>
</zoo>

Upvotes: 4

Views: 2089

Answers (2)

Tomalak
Tomalak

Reputation: 338376

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:a="http://example.com/abc"
    exclude-result-prefixes="a"
>
    <xsl:output method="xml" indent="yes"/>

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

    <xsl:template match="a:* | @a:*" />
</xsl:stylesheet>

Empty templates match nodes but do not output anything, effectively removing what they match.

Upvotes: 7

halfer
halfer

Reputation: 20487

(Posted solution on behalf of the question author to move the answer to the answer space).

Many thanks to Tomalak for the correct XSL syntax, and to Ian Roberts for pointing out that in order to use namespaces in my XSLT, I need to call "setNamespaceAware(true)" at the very beginning, in my DocumentBuilderFactory.

XSLT (thanks to Tomalak):

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:a="http://example.com/abc"
    exclude-result-prefixes="a"
>
    <xsl:output method="xml" indent="yes"/>

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

    <xsl:template match="a:* | @a:*" />
</xsl:stylesheet>

Java program that successfully does the XSLT filtering by namespace:

import java.io.*;
import org.w3c.dom.*; // XML DOM
import javax.xml.parsers.*; // DocumentBuilder, etc
import javax.xml.transform.*; // Transformer, etc
import javax.xml.transform.stream.*; // StreamResult, StreamSource, etc
import javax.xml.transform.dom.DOMSource;

public class Test {

    public static void main(String[] args) {
        new Test().testZoo();
    }

public void testZoo () {
    String zooXml = Test.readXmlFile ("zoo.xml");
    if (zooXml == null)
        return;
    
    try {
        // Create a new document builder factory, and make sure it[s namespace-aware 
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilder docBuilder = dbf.newDocumentBuilder ();

        // Read XFDD input string into DOM
        Document xfddDoc = 
            docBuilder.parse(new StringBufferInputStream (zooXml));
    
        // Filter out all elements in "http://example.com/abc" namespace
        StreamSource styleSource = new StreamSource (new File ("zoo.xsl"));
        Transformer transformer =
            TransformerFactory.newInstance().newTransformer (styleSource);
    
        // Convert final DOM back to String
        StringWriter buffer = new StringWriter ();
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,  "yes"); // Remember: we want to insert this XML as a subnode
        transformer.setOutputProperty(OutputKeys.INDENT,  "yes");
        transformer.transform(new DOMSource(xfddDoc), new StreamResult (buffer));
        String translatedXml = buffer.toString();
    }
    catch (Exception e) {
        System.out.println ("convertTransactionData error: " + e.getMessage());
        e.printStackTrace ();
    }
}

Upvotes: 1

Related Questions