5

I would like to remove the xmlns attribute from following xml string. I have written a java program but not sure if it does what needs to be done here.

How do I remove the xmlns attribute and get the modified xml string ?

Input XML string:

<?xml version="1.0" encoding="UTF-8"?>
<Payment xmlns="http://api.com/schema/store/1.0">
    <Store>abc</Store>
</Payment>

Expected XML Output string:

<?xml version="1.0" encoding="UTF-8"?>
<Payment>
    <Store>abc</Store>
</Payment>

Java Class:

public class XPathUtils {

    public static void main(String[] args) {
        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Payment xmlns=\"http://api.com/schema/store/1.0\"><Store>abc</Store></Payment>";
        String afterNsRemoval = removeNameSpace(xml);
        System.out.println("afterNsRemoval = " + afterNsRemoval);
    }

    public static String removeNameSpace(String xml) {
        try {
            System.out.println("before xml = " + xml);
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            InputSource inputSource = new InputSource(new StringReader(xml));
            Document xmlDoc = builder.parse(inputSource);
            Node root = xmlDoc.getDocumentElement();
            NodeList rootchildren = root.getChildNodes();
            Element newroot = xmlDoc.createElement(root.getNodeName());
            for (int i = 0; i < rootchildren.getLength(); i++) {
                newroot.appendChild(rootchildren.item(i).cloneNode(true));
            }
            xmlDoc.replaceChild(newroot, root);
            return xmlDoc.toString();
        } catch (Exception e) {
            System.out.println("Could not parse message as xml: " + e.getMessage());
        }
        return "";
    }
}

Output:

before xml = <?xml version="1.0" encoding="UTF-8"?><Payment xmlns="http://api.com/schema/store/1.0"><Store>abc</Store></Payment>

afterNsRemoval = [#document: null]
Nital
  • 5,784
  • 26
  • 103
  • 195
  • I will be more than happy to try it out. Any pointers on how can this be implemented? – Nital May 20 '16 at 19:54
  • gimme a few minutes... the beauty with xpath is that you can effectively remove all attributes of your choosing... even for a very complex document ... the xpath is no more than "//@*"... – vtd-xml-author May 20 '16 at 19:56

5 Answers5

3

You need a transformer. Check below the modified method :

public static String removeNameSpace(String xml) {


        try {
             TransformerFactory tf = TransformerFactory.newInstance();
             Transformer transformer = tf.newTransformer();
             transformer.setOutputProperty( OutputKeys.METHOD, "xml" );
             transformer.setOutputProperty( OutputKeys.INDENT, "false" );
            System.out.println("before xml = " + xml);
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            InputSource inputSource = new InputSource(new StringReader(xml));
            Document xmlDoc = builder.parse(inputSource);
            Node root = xmlDoc.getDocumentElement();
            NodeList rootchildren = root.getChildNodes();
            Element newroot = xmlDoc.createElement(root.getNodeName());
            for (int i = 0; i < rootchildren.getLength(); i++) {
                newroot.appendChild(rootchildren.item(i).cloneNode(true));
            }
            xmlDoc.replaceChild(newroot, root);
            DOMSource requestXMLSource = new DOMSource( xmlDoc.getDocumentElement() );
            StringWriter requestXMLStringWriter = new StringWriter();
            StreamResult requestXMLStreamResult = new StreamResult( requestXMLStringWriter );            
            transformer.transform( requestXMLSource, requestXMLStreamResult );
            String modifiedRequestXML = requestXMLStringWriter.toString();

            return modifiedRequestXML;
        } catch (Exception e) {
            System.out.println("Could not parse message as xml: " + e.getMessage());
        }
        return "";
    }

Output :

before xml = <?xml version="1.0" encoding="UTF-8"?><Payment xmlns="http://api.com/schema/store/1.0"><Store>abc</Store></Payment>
afterNsRemoval = <?xml version="1.0" encoding="UTF-8"?><Payment><Store>abc</Store></Payment>
SomeDude
  • 13,876
  • 5
  • 21
  • 44
2

Consider XSLT as removing declared/undeclared namespace is a regular task. Here, no third-party modules are needed as base Java is equipped with an XSLT 1.0 processor. Additionally, no loops or XML tags/attribs re-creation is handled as XSLT takes care of the transformation.

import java.io.*;

import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class XPathUtils {
    public static void main(String[] args) {

        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Payment xmlns=\"http://api.com/schema/store/1.0\"><Store>abc</Store></Payment>";
        String afterNsRemoval = removeNameSpace(xml);
        System.out.println("afterNsRemoval = " + afterNsRemoval);
    }

    public static String removeNameSpace(String xml) {
        try{
            String xslStr = String.join("\n",
                "<xsl:transform xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">",
                "<xsl:output version=\"1.0\" encoding=\"UTF-8\" indent=\"no\"/>",
                "<xsl:strip-space elements=\"*\"/>",                          
                "  <xsl:template match=\"@*|node()\">",
                "   <xsl:element name=\"{local-name()}\">",
                "     <xsl:apply-templates select=\"@*|node()\"/>",
                "  </xsl:element>",
                "  </xsl:template>",  
                "  <xsl:template match=\"text()\">",
                "    <xsl:copy/>",
                "  </xsl:template>",                                  
                "</xsl:transform>");

            // Parse XML and Build Document
            DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            InputSource is = new InputSource();
            is.setCharacterStream(new StringReader(xml));
            Document doc = db.parse (is);                      

            // Parse XSLT and Configure Transformer
            Source xslt = new StreamSource(new StringReader(xslStr));
            Transformer tf = TransformerFactory.newInstance().newTransformer(xslt);

            // Output Result to String
            DOMSource source = new DOMSource(doc);
            StringWriter outWriter = new StringWriter();
            StreamResult strresult = new StreamResult( outWriter );        
            tf.transform(source, strresult);
            StringBuffer sb = outWriter.getBuffer(); 
            String finalstring = sb.toString();

            return(finalstring);

        } catch (Exception e) {
            System.out.println("Could not parse message as xml: " + e.getMessage());
        }
            return "";    
    }
}
Parfait
  • 104,375
  • 17
  • 94
  • 125
  • Doesn't copy attributes, who knows what else. I couldn't revoke vote up – misterti Sep 02 '20 at 18:31
  • @misterti, what did you need to do? This XSLT solution is intended to removes namespaces. See demo using OP's XML and XSLT in Java code: https://xsltfiddle.liberty-development.net/3Mvnt3X – Parfait Sep 02 '20 at 19:05
  • It also removes all other attributes and changes them to tags. This is unacceptable modification – misterti Sep 03 '20 at 08:00
  • @misterti, how about this version using identity transform: https://xsltfiddle.liberty-development.net/3Mvnt3X/1? – Parfait Sep 03 '20 at 13:54
1

You have done everything right except last action: document.toString() gives you incorrect result, because it is incorrect way of producing string xml representation of Document object. See this answer, it contains correct implementation of this method:

public static String toString(Document doc) {
    try {
        StringWriter sw = new StringWriter();
        TransformerFactory tf = TransformerFactory.newInstance();
        ...
Community
  • 1
  • 1
Andremoniy
  • 34,031
  • 20
  • 135
  • 241
0

This is the code to do it with XPath and vtd-xml( of which I am the author)...

import com.ximpleware.*;
import java.io.*;

public class removeAttrNode {
    public static void main(String[] s) throws VTDException, Exception{
        VTDGen vg = new VTDGen(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Payment xmlns=\"http://api.com/schema/store/1.0\"><Store>abc</Store></Payment>";
        vg.setDoc(xml.getBytes());
        vg.parse(false); // turn off namespace awareness so that 
        VTDNav vn = vg.getNav();
        AutoPilot ap = new AutoPilot(vn);
        XMLModifier xm = new XMLModifier(vn);
        ap.selectXPath("//@xmlns");
        int i=0;
        while((i=ap.evalXPath())!=-1){
            xm.remove();
        }
        xm.output(baos);
        System.out.println(baos.toString());    
    }
}
vtd-xml-author
  • 3,319
  • 4
  • 22
  • 30
0

You should get getOwnerDocument() for current ELEMENT_NODE and ownerDoc.renameNode(node,null,node.getLocalName()). You should also ownerDoc.renameNode(subNode,null,subNode.getLocalName()) for all attributes you are need and removeNamedItemNS for attributes you need not.