Strip whitespace and newlines from XML in Java
Asked Answered
R

8

21

Using Java, I would like to take a document in the following format:

<tag1>
 <tag2>
    <![CDATA[  Some data ]]>
 </tag2>
</tag1>

and convert it to:

<tag1><tag2><![CDATA[  Some data ]]></tag2></tag1>

I tried the following, but it isn't giving me the result I am expecting:

DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
dbfac.setIgnoringElementContentWhitespace(true);
DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
Document doc = docBuilder.parse(new FileInputStream("/tmp/test.xml"));

Writer out = new StringWriter();
Transformer tf = TransformerFactory.newInstance().newTransformer();
tf.setOutputProperty(OutputKeys.INDENT, "no");
tf.transform(new DOMSource(doc), new StreamResult(out));
System.out.println(out.toString());
Rojo answered 28/7, 2012 at 18:32 Comment(8)
You could treat this as a text file, open it with a BufferedReader, read every line and save its trim value inside a StringBuilder, after all this, use a BufferedWriter to save your file with the contents of the StingBuilder.Tachygraphy
If you're willing to go to something like Xerces-J you can use OutputFormat to not pretty print the results : xerces.apache.org/xerces-j/apiDocs/org/apache/xml/serialize/…Drusilla
btw, the reason the setIgnoringElementContentWhitespace doesn't help is because you must be using XML Schema/DTD validation in order for the parser to know what whitespace is ignorable.Solitary
@LuiggiMendoza - you should never be manually editing xml data like that. you are just asking to screw it up.Solitary
@Dataknife I tested your solution. Unfortunately It didn't work either.Rojo
I'm not sure what else to tell you, that's been how I've done it in the past- short of using JAXB Marshallers to serialize the objects from a data model...Drusilla
Your "some data" can contains line breaks?Tosh
Doesn't answer the question, but if any Scala users land here, there is scala.xml.Utility.trim().Angleaangler
V
23

Working solution following instructions in the question's comments by @Luiggi Mendoza.

public static String trim(String input) {
    BufferedReader reader = new BufferedReader(new StringReader(input));
    StringBuffer result = new StringBuffer();
    try {
        String line;
        while ( (line = reader.readLine() ) != null)
            result.append(line.trim());
        return result.toString();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}
Viminal answered 31/10, 2012 at 17:52 Comment(3)
Note that you should close the BufferedReader after use by using finally or Java 8 try-with-resources.Bolter
@Bolter That would look cleaner for code analysis tools, but in this case (a StringReader and a BufferedReader), there is no real resource to free. You'd just assign null to some fields of objects immediately eligible for garbage collection anyway. Having said that, I would close them, too.Abessive
As mentioned in comments to the question, this solution is broken if the CDATA section contains linefeeds which should be preserved.Abessive
S
15

recursively traverse the document. remove any text nodes with blank content. trim any text nodes with non-blank content.

public static void trimWhitespace(Node node)
{
    NodeList children = node.getChildNodes();
    for(int i = 0; i < children.getLength(); ++i) {
        Node child = children.item(i);
        if(child.getNodeType() == Node.TEXT_NODE) {
            child.setTextContent(child.getTextContent().trim());
        }
        trimWhitespace(child);
    }
}
Solitary answered 28/7, 2012 at 18:56 Comment(2)
This removes spaces in the node - the example has no spaces in the text nodesCestus
@Mark - actually it does. the contents of "tag2" involve leading newlines and spaces and trailing newlines and spaces.Solitary
B
6

As documented in an answer to another question, the relevant function would be DocumentBuilderFactory.setIgnoringElementContentWhitespace(), but - as pointed out here already - that function requires the use of a validating parser, which requires an XML schema, or some such.

Therefore, your best bet is to iterate through the Document you get from the parser, and remove all nodes of type TEXT_NODE (or those TEXT_NODEs which contain only whitespace).

Bandstand answered 17/3, 2013 at 16:6 Comment(1)
... and the answers by jtahlborn and buergi demonstrate how to do exactly that.Effie
A
2

I support @jtahlborn's answer. Just for completeness, I adapted his solution to completely remove the whitespace-only elements instead of just clearing them.

public static void stripEmptyElements(Node node)
{
    NodeList children = node.getChildNodes();
    for(int i = 0; i < children.getLength(); ++i) {
        Node child = children.item(i);
        if(child.getNodeType() == Node.TEXT_NODE) {
            if (child.getTextContent().trim().length() == 0) {
                child.getParentNode().removeChild(child);
                i--;
            }
        }
        stripEmptyElements(child);
    }
}
Azaria answered 3/11, 2020 at 8:59 Comment(3)
Excellent. This is exactly what I needed, too.Effie
Since Java 11, you can write isblank() instead of trim().length() == 0 and you can use node instead of child.getParentNode(). What I don't understand is why you decrement i.Strenuous
Ah, of course, children.getLength() is reduced, so i must be reset to obtain the next child.Strenuous
A
0

Java8+transformer does not create any but Java10+transformer puts everywhere empty lines. I still want to keep a pretty indents. This is my helper function to create xml string from any DOMElement instance such as doc.getDocumentElement() root node.

public static String createXML(Element elem) throws Exception {
        DOMSource source = new DOMSource(elem);
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        //transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
        //transformer.setOutputProperty("http://www.oracle.com/xml/is-standalone", "yes");
        transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC,"yes");
        transformer.setOutputProperty("http://www.oracle.com/xml/is-standalone", "yes");
        transformer.transform(source, result);

        // Java10-transformer adds unecessary empty lines, remove empty lines
        BufferedReader reader = new BufferedReader(new StringReader(writer.toString()));
        StringBuilder buf = new StringBuilder();
        try {
            final String NL = System.getProperty("line.separator", "\r\n");
            String line;
            while( (line=reader.readLine())!=null ) {
                if (!line.trim().isEmpty()) {
                    buf.append(line); 
                    buf.append(NL);
                }
            }
        } finally {
            reader.close();
        }
        return buf.toString();  //writer.toString();
    }
Ahasuerus answered 13/2, 2019 at 10:12 Comment(0)
M
0

In order to strip whitespace and newlines from XML in Java try the following solution which uses StringBuffer() and conditional logic:

public static String LimpaXML(String xml) {
    StringBuffer result = new StringBuffer();
    char c_prev = '\0';
    xml = xml.trim();
    int len = xml.length();
    for (int i=0; i<len; i++) {
        char c = xml.charAt(i);
        char c_next = (i+1 < len) ? xml.charAt(i+1) : '\0';
        if (c == '\n') continue;
        if (c == '\r') continue;
        if (c == '\t') c = ' ';
        if (c == ' ') {
            if (c_prev == ' ') continue;
            if (c_next == '\0') continue;
            if (c_prev == '>') continue;
            if (c_next == '>') continue;
        }
        result.append(c);
        c_prev = c;
    }
    return result.toString();
}
Monophagous answered 10/7, 2022 at 14:39 Comment(0)
B
0

No one seems to have mentioned the possibility of doing a simple XSLT transformation. In XSLT 3.0:

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:strip-space elements="*"/>
 <xsl:mode on-no-match="shallow-copy"/>
</xsl:transform>
Burrus answered 10/5 at 21:1 Comment(2)
Should 3rd line be: xsl:mode on-no-match="shallow-copy"?Rojo
Fixed as suggested.Burrus
F
-4

Try this code. read and write methods in FileStream ignore whitespace and indents.

try {
    File f1 = new File("source.xml");
    File f2 = new File("destination.xml");
    InputStream in = new FileInputStream(f1);  
    OutputStream out = new FileOutputStream(f2);

    byte[] buf = new byte[1024];
    int len;
    while ((len = in.read(buf)) > 0){
    out.write(buf, 0, len);
}
in.close();
out.close();
System.out.println("File copied.");
} catch(FileNotFoundException ex){
    System.out.println(ex.getMessage() + " in the specified directory.");
    System.exit(0);
} catch(IOException e7){
    System.out.println(e7.getMessage());  
}
Fury answered 28/7, 2012 at 19:4 Comment(1)
WOOOW code quality like decompiled, the code does not strip blanks at allSlither

© 2022 - 2024 — McMap. All rights reserved.