Summary: I want to save a org.w3c.dom.Document to file with nice indentation (pretty print it). The below code with a Transformer does the job in some cases, but not in all cases (see example). Can you help me fix this?
I have a org.w3c.dom.Document (not org.jdom.Document) and want to automatically format it nicely and print it into a file. How can I do that? I tried this, but it doesn't work if there are additional newlines in the document:
import java.io.ByteArrayInputStream;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
public class Main {
public static void main(String[] args) {
try {
String input = "<asdf>\n\n<a>text</a></asdf>";
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(input.getBytes()));
System.out.println("-- input -------------------\n" + input + "\n----------------------------");
System.out.println("-- output ------------------");
prettify(doc);
System.out.println("----------------------------");
} catch (Exception e) {}
}
public static void prettify(Document doc) {
try {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
transformer.transform(new DOMSource(doc), new StreamResult(System.out));
} catch (Exception e) {}
}
}
I have directed the ouput to System.out so that you can run it easily wherever you want (for instance on Ideone.com). You can see, that the output is not pretty. If I remove the \n\n from the input string, everything is fine. And the document usually doesn't come from a string, but from a file and gets modified heavily before I want to prettify it.
This Transformer seems to be the right way, but I am missing something. Can you tell me, what I am doing wrong?
SSCCE output:
-- input -------------------
<asdf>
<a>text</a></asdf>
----------------------------
-- output ------------------
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<asdf>
<a>text</a>
</asdf>
----------------------------
Expected output:
-- input -------------------
<asdf>
<a>text</a></asdf>
----------------------------
-- output ------------------
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<asdf>
<a>text</a>
</asdf>
----------------------------
Try this:
It needs org.apache.xml.serialize.XMLSerializer and org.apache.xml.serialize.OutputFormat ;
OutputFormat format = new OutputFormat(document); //document is an instance of org.w3c.dom.Document
format.setLineWidth(65);
format.setIndenting(true);
format.setIndent(2);
Writer out = new StringWriter();
XMLSerializer serializer = new XMLSerializer(out, format);
serializer.serialize(document);
String formattedXML = out.toString();
Related
Im trying to insert a comment with syntax version information before the first node of an xml document but for some reason it always gets appended after the last element. I cant use other libraries like XStream and such.
I have already tried to do this via the append and the insertBefore method but both of them yield the same result. Is it possible to do this just with the plain Document, Comment and Node or do I have to use a Transformer?
Document doc = XmlDocumentUtil.createDocument();
//Create root node
Node rootNode = doc.createElement(NODE_DATA_CONFIGURATION);
doc.appendChild(rootNode);
//Create syntax identification comment
Comment syntaxIdentification = doc.createComment(writeSyntaxIdentificationQE);
doc.insertBefore(syntaxIdentification, rootNode);
//Create revision information
Element modificationNumber = doc.createElement("modificationNumber");
modificationNumber.setTextContent(String.valueOf(configTable.getModificationNumber()));
rootNode.appendChild(modificationNumber);
Element modificationTime = doc.createElement("modificationTime");
modificationTime.setTextContent(configTable.getModificationTime());
rootNode.appendChild(modificationTime);
Element modifier = doc.createElement("modifier");
modifier.setTextContent(configTable.getModifier());
rootNode.appendChild(modifier);
for (...) {
... some data generation
}
What I get:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<DataConfiguration>
<modificationNumber>2</modificationNumber>
<modificationTime>2019-07-25 07:42:28.804 +0200</modificationTime>
<modifier>testuser</modifier>
<someData idxFrom="4" idxTo="9"
signalName="1" signalOffset="273.15" signalTemplate="asdf" skip="Y"/>
</DataConfiguration>
<!--SyntaxIdentification: 1.0-->
What I need:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--SyntaxIdentification: 1.0-->
<DataConfiguration>
<modificationNumber>2</modificationNumber>
<modificationTime>2019-07-25 07:42:28.804 +0200</modificationTime>
<modifier>testuser</modifier>
<someData idxFrom="4" idxTo="9"
signalName="1" signalOffset="273.15" signalTemplate="asdf" skip="Y"/>
</DataConfiguration>
To me your code seems good overall. Seems like you are missing only text nodes.
Here is full working code.
package xml;
import java.io.StringWriter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.*;
public class XMLComment {
public static void main(String[] args) throws ParserConfigurationException, TransformerException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.newDocument();
// Create root node
Node rootNode = doc.createElement("DataConfiguration");
doc.appendChild(rootNode);
// Create syntax identification comment
Comment syntaxIdentification = doc.createComment("SyntaxIdentification: 1.0");
doc.insertBefore(syntaxIdentification, rootNode);
// Create modificationNumber
Node modificationNumber = doc.createElement("modificationNumber");
Text mnText = doc.createTextNode("123456");
modificationNumber.appendChild(mnText);
// Create modificationTime
Element modificationTime = doc.createElement("modificationTime");
Text mtText = doc.createTextNode("2019-07-25 07:42:28.804 +0200");
modificationTime.appendChild(mtText);
rootNode.appendChild(modificationNumber);
rootNode.appendChild(modificationTime);
printXML(doc);
}
private static void printXML(Document doc) throws TransformerException {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
// initialize StreamResult with File object to save to file
StreamResult result = new StreamResult(new StringWriter());
DOMSource source = new DOMSource(doc);
transformer.transform(source, result);
String xmlString = result.getWriter().toString();
System.out.println(xmlString);
}
}
It will output, close to what exactly you are looking for.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--SyntaxIdentification: 1.0--><DataConfiguration>
<modificationNumber>123456</modificationNumber>
<modificationTime>2019-07-25 07:42:28.804 +0200</modificationTime>
</DataConfiguration>
Hope it helps.
This question already has answers here:
Modify the content of a file using Java
(6 answers)
Closed 5 years ago.
I have the below code that writes to an xml file, the problem that I am having is that it creates a new file every time i write to it and overwrites the other data saved in the file. I am looking for a solution that would append to the existing file instead. How do I modify this code to append to the file each time instead of overwrite? Also, I am using the netbeans IDE to run this program.
import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
public class WriteXMLFile {
public static void main() throws ParserConfigurationException,SAXException,Exception
{
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();//
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();//
Document doc = docBuilder.newDocument();// this is difrent
Element rootElement = doc.createElement("Contacts");//
doc.appendChild(rootElement); // this is difrent
// staff elements
Element Contact1 = doc.createElement("Contact1");
rootElement.appendChild(Contact1);
// set attribute to staff element
Contact1.setAttribute("id","1");
// firstname elements
Element firstname = doc.createElement("Name");
firstname.appendChild(doc.createTextNode(EmailFrame.name.getText()));
Contact1.appendChild(firstname);
//Email Element
Element email = doc.createElement("Email");
email.appendChild(doc.createTextNode(EmailFrame.email.getText()));
Contact1.appendChild(email);
// phone element
Element phone= doc.createElement("Phone");
phone.appendChild(doc.createTextNode(EmailFrame.phone.getText()));
Contact1.appendChild(phone);
//id element
Element id = doc.createElement("ID");
id.appendChild(doc.createTextNode(EmailFrame.id.getText()));
Contact1.appendChild(id);
try{
// write the content into xml file
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File("C:/Users/steve/Desktop/xmlemail/Email.xml"));
transformer.transform(source, result);
System.out.println("File saved!");
}
catch (TransformerException tfe) {
tfe.printStackTrace();
}
}
}
What you need to do is read the contents of the xml file into an object first before writing, then append your new content to your object then write your object to the xml file
Look at this resource for reading an xml file it should help
https://www.mkyong.com/java/how-to-read-xml-file-in-java-dom-parser/
I've been looking around everywhere for Java samples to transform an XML document with an XSLT. I've found several samples using new File("path/to/file.xml") to load in the XML and the XSLT and those work great. My problem is that I'm trying to use this in a new method that will accept two org.w3c.dom.Document objects. As soon as I replace the StreamSource used to load in the XSLT with a DOMSource the result of my call is then the XSLT instead of the transformed XML.
Working code from How to call XSL template from java code?:
Source xmlInput = new StreamSource(new File("c:/path/to/input.xml"));
Source xsl = new StreamSource(new File("c:/path/to/file.xsl"));
Result xmlOutput = new StreamResult(new File("c:/path/to/output.xml"));
try {
Transformer transformer = TransformerFactory.newInstance().newTransformer(xsl);
transformer.transform(xmlInput, xmlOutput);
} catch (TransformerException e) {
// Handle.
}
My code:
public static Document transformXML(Document xml, Document xslt) throws TransformerException, UnsupportedEncodingException, SAXException, IOException, ParserConfigurationException, FactoryConfigurationError{
Source xmlSource = new DOMSource(xml);
Source xsltSource = new DOMSource(xslt);
StreamResult result = new StreamResult(new StringWriter());
// the factory pattern supports different XSLT processors
TransformerFactory transFact =
TransformerFactory.newInstance();
Transformer trans = transFact.newTransformer(xsltSource);
trans.transform(xmlSource, result);
Document resultDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(result.getWriter().toString().getBytes("utf-8")));
return resultDoc;
}
My result document is then the XSLT instead of the XML. What am I doing wrong with the DOMSource?
XSLT and XPath only make sense with a namespace aware DOM implementation and DOM tree, that is why I asked "Are the DOM trees you feed to the transformer built with a namespace aware document builder?" in my comment.
As far as I have tested with Oracle Java 1.8, when a not namespace-aware DocumentBuilderFactory and the built-in Transformer is used, your method returns the stylesheet code. However as soon as I change the DocumentBuilderFactory to be namespace aware, the result is as intended.
Here is the working sample:
package domsourcetest1;
import java.io.IOException;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import org.w3c.dom.Document;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.xml.sax.SAXException;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSSerializer;
/**
*
* #author Martin Honnen
*/
public class DOMSourceTest1 {
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException, TransformerException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document xslt = db.parse("sheet1.xsl");
Document xml = db.newDocument();
xml.appendChild(xml.createElementNS(null, "root"));
Document result = transformXML(xml, xslt);
System.out.println(result.getDocumentElement().getTextContent());
LSSerializer serializer = ((DOMImplementationLS) xml.getImplementation()).createLSSerializer();
System.out.println(serializer.writeToString(result));
}
public static Document transformXML(Document xml, Document xslt) throws TransformerException, ParserConfigurationException, FactoryConfigurationError {
Source xmlSource = new DOMSource(xml);
Source xsltSource = new DOMSource(xslt);
DOMResult result = new DOMResult();
// the factory pattern supports different XSLT processors
TransformerFactory transFact
= TransformerFactory.newInstance();
Transformer trans = transFact.newTransformer(xsltSource);
trans.transform(xmlSource, result);
Document resultDoc = (Document) result.getNode();
return resultDoc;
}
}
The sample stylesheet simply outputs information about the XSLT processor:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<debug>
<xsl:value-of select="system-property('xsl:vendor')"/>
</debug>
</xsl:template>
</xsl:stylesheet>
Output of the program is
Apache Software Foundation (Xalan XSLTC)
<?xml version="1.0" encoding="UTF-16"?>
<debug>Apache Software Foundation (Xalan XSLTC)</debug>
Now when I comment out //dbf.setNamespaceAware(true); in the main method the result is
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:template match="/"><debug><xsl:value-of select="system-property('xsl:vendor')"/></debug></xsl:template></xsl:stylesheet>
meaning the result is indeed the stylesheet document. That is obviously a bug or at least a quirk with the built-in Xalan Transformer, when I put Saxon 6.5.5 on the class path the problem does not occur, nor does it occur with Saxon 9.6 on the class path.
In general, however, I don't think you will get meaningful results when using XSLT or XPath with not namespace aware DOM trees. See also the DOM2DOM sample in the Xalan release http://svn.apache.org/viewvc/xalan/java/tags/xalan-j_2_7_2/samples/DOM2DOM/DOM2DOM.java?revision=1695338&view=markup which says
// And setNamespaceAware, which is required when parsing xsl files
dFactory.setNamespaceAware(true);
Using Java DOM I'm trying to set an attribute for an element without the namespace before the attribute name.
So, what I need is:
<documentObject xmlns="http://www.myschema.com">
<element1 attr1="value">foo</element1>
</documentObject>
If I try to set the attribute as following element1.setAttributeNS("http://www.myschema.com", "attr1", value); I get an empty xmlns tag and additionaly a xmlns with prefix like the following:
<element1 attr1="value" xmlns="" xmlns:ns3="http://www.myschema.com">foo</element1>
If I try to set the attribute as following element1.setAttribute("xmlns:attr1", value); I get a prefix (xmlns) before my attribute name as shown here:
<element1 xmlns:attr1="value">foo</element1>
As for further information I create my elements as following:
Element element = dom.createElementNS("http://www.myschema.com", elemName);
element.appendChild(dom.createCDATASection("foo");
xmlElement.appendChild(element);
Let's look at your desired output again:
<documentObject xmlns="http://www.myschema.com">
<element1 attr1="value">foo</element1>
</documentObject>
In this document, the following statements are true:
documentObject and element1 are in the http://www.myschema.com namespace.
The attribute attr1 is not in any namespace.
While elements whose names are not prefixed are going to be in whatever default namespace is in effect, attributes whose names are not prefixed are not in any namespaces. See the spec:
Default namespace declarations do not apply directly to attribute names; the interpretation of unprefixed attributes is determined by the element on which they appear.
So to obtain the output you desire, you should be able to just do:
element1.setAttribute("attr1", value);
Of course this all depends on the desired output being correct. If really attr1 must be in a namespace, then your desired output is incorrect.
Below code will produce output
<?xml version="1.0" encoding="UTF-8"?>
<documentObject xmlns="http://www.myschema.com">
<element1 attr1="value">foo</element1>
</documentObject>
Java Code
import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class XMLTest {
public static void main(String[] args) throws ParserConfigurationException, TransformerException {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
Element element = doc.createElementNS("http://www.myschema.com", "documentObject");
doc.appendChild(element);
Element element1 = doc.createElement("element1");
element.appendChild(element1);
element1.appendChild(doc.createTextNode("foo"));
Attr attr = doc.createAttribute("attr1");
attr.setValue("value");
element1.setAttributeNode(attr);
element.appendChild(element1);
// write the content into xml file
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File("testfile.xml"));
transformer.transform(source, result);
System.out.println("File saved!");
}
}
I would like to know how do I overwrite and existing element in Java using dom parser. for example, I have the file
I am trying to overwrite the element and replace it with the element
Thanks.
Algorithm is easy:
Read a file and parse it to XML document
Make any changes you want
Overwrite existing file with new content
See my example:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class DomProgram {
public static void main(String[] args) throws Exception {
File xmlFile = new File("C:\\test.xml");
Document document = readXmlDocument(xmlFile);
makeChanges(document);
Transformer transformer = createXmlTransformer();
overwriteXmlFile(xmlFile, document, transformer);
}
private static void overwriteXmlFile(File xmlFile, Document document,
Transformer transformer) throws FileNotFoundException,
TransformerException {
StreamResult result = new StreamResult(new PrintWriter(
new FileOutputStream(xmlFile, false)));
DOMSource source = new DOMSource(document);
transformer.transform(source, result);
}
private static Transformer createXmlTransformer() throws Exception {
Transformer transformer = TransformerFactory.newInstance()
.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
return transformer;
}
private static void makeChanges(Document document) {
Element root = document.getDocumentElement();
root.setAttribute("test", "testvalue");
}
private static Document readXmlDocument(File xmlFile) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(xmlFile);
return document;
}
}
Example XML file:
<note>
<to>Ebele</to>
<from>mykhaylo</from>
<heading>Reminder</heading>
<body>Read about problem before you ask ;)</body>
</note>
Also see:
Simple Java DOM XML Example
How To Read XML File In Java – (DOM Parser)