XML Transformation using DOMSource, DOMResult & XSLT - java

I am trying to transform the following XML
<PHONEBOOK>
<PERSON>
<NAME>Ren1</NAME>
<EMAIL>ren1#gmail.com</EMAIL>
<TELEPHONE>999-999-9999</TELEPHONE>
<WEB>www.ren1.com</WEB>
</PERSON>
<PERSON>
<NAME>Ren2</NAME>
<EMAIL>ren2#gmail.com</EMAIL>
<TELEPHONE>999-999-9999</TELEPHONE>
<WEB>www.ren2.com</WEB>
</PERSON>
<PERSON>
<NAME>Ren3</NAME>
<EMAIL>ren3#gmail.com</EMAIL>
<TELEPHONE>999-999-9999</TELEPHONE>
<WEB>www.ren3.com</WEB>
</PERSON>
</PHONEBOOK>
to
<Names><Name>Ren1</Name><Name>Ren2</Name><Name>Ren3</Name></Names>
using DOMSource, DOMResult and XSLT.
XSLT used is as follows
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output omit-xml-declaration="yes" method="xml"></xsl:output>
<xsl:template match="/">
<Names>
<xsl:for-each select="PHONEBOOK/PERSON">
<Name>
<xsl:value-of select="NAME" />
</Name>
</xsl:for-each>
</Names>
Java Code used for transformation:
package test1;
import java.io.IOException;
import java.io.StringWriter;
import java.io.ObjectInputStream.GetField;
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.DOMResult;
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.w3c.dom.Node;
import org.xml.sax.SAXException;
public class Test2 {
public static void main(String[] args) throws TransformerException,
ParserConfigurationException, SAXException, IOException {
// TODO Auto-generated method stub
//Stylesheet
StreamSource stylesource = new StreamSource(
"src/test1/transform_stylesheet1.xsl");
DocumentBuilderFactory docbFactory = DocumentBuilderFactory
.newInstance();
DocumentBuilder dBuilder = docbFactory.newDocumentBuilder();
//source XML
Document sourceDoc = dBuilder.parse("src/test1/Sample1.xml");
DOMSource source = new DOMSource(sourceDoc);
TransformerFactory transformerFactory = TransformerFactory
.newInstance();
Transformer transformer = transformerFactory
.newTransformer(stylesource);
Document document = dBuilder.newDocument();
DOMResult result = new DOMResult(document);
transformer.transform(source, result);
Node resultDoc = ((Document) result.getNode()).getDocumentElement();
System.out.println(resultDoc.getChildNodes().getLength());
// print the result
StringWriter writer = new StringWriter();
transformer.transform(new DOMSource(resultDoc), new StreamResult(writer));
String str = writer.toString();
System.out.println(str);
}
}
Output of the above is as follows:
3 <Names/>
but i expect,
3
<Names><Name>Ren1</Name><Name>Ren2</Name><Name>Ren3</Name></Names>
i debugged the code and found that 'resultDoc' has the content which i expect. Am i missing something while printing the result?

Your problem is that you're using the same transformer for the stylesheet processing and the output. That means, the stylesheet is applied again, but this time to the <Names><Name>Ren1</Name>...</Names> xml. You can imagine that this doesn't give the results you want.
Change your code to:
// print the result
StringWriter writer = new StringWriter();
Transformer transformer2 = transformerFactory.newTransformer();
transformer2.transform(new DOMSource(resultDoc), new StreamResult(writer));
String str = writer.toString();
System.out.println(str);
and it should work.
As #Abel mentions, you can also do the stylesheet processing and the to String in one go:
StringWriter writer = new StringWriter();
transformer.transform(source, new StreamResult(writer));
String str = writer.toString();
System.out.println(str);
You don't need the DOMResult and DOMSource variables then.

Related

XMLStreamWriter write tag containing colon

When I use colon in the tag name like in the example below, it ends up in error (there is no problem with tags without the colon).
package test;
import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
public class SomeClass{
public StringWriter test() throws XMLStreamException, TransformerConfigurationException, TransformerException {
StringWriter stringOut = new StringWriter();
XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(stringOut);
xmlWriter.writeStartDocument("UTF-8", "1.0");
xmlWriter.writeStartElement("SomeWordHere");
{
xmlWriter.writeStartElement("SomeName:enable");//<--- notice the colon
xmlWriter.writeCharacters("true");
xmlWriter.writeEndElement();
}
xmlWriter.writeEndElement();
xmlWriter.writeEndDocument();
xmlWriter.flush();
xmlWriter.close();
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.STANDALONE, "no");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
StringWriter formattedStringWriter = new StringWriter();
transformer.transform(new StreamSource(new StringReader(stringOut.toString())), new StreamResult(formattedStringWriter));
return formattedStringWriter;
}
}
How to write the tag that would still conain the colon and would not end up in error?
I am trying to emulate the XML output (Collada DAE) produced by LEGO Stud.io software, there are sections like the one below containing tag names with colons.
<library_materials>
<material id="material_id_7" name="SOLID-BLUE">
<instance_effect url="#effect_id_7-fx" />
<extra>
<technique profile="eyesight">
<ScratchBump:enable> true </ScratchBump:enable>
<MinScratchStrength:value> 0 </MinScratchStrength:value>
<MaxScratchStrength:value> 0.2 </MaxScratchStrength:value>
<BigScratch:enable> true </BigScratch:enable>
<SmallScratch:enable> true </SmallScratch:enable>
</technique>
</extra>
</material>
</library_materials>
Colon is used for namespaces and per "Namespaces in XML" specification, it cannot be used in entity names.
The specification states:
[Definition: A document is namespace-well-formed if it conforms to
this specification. ]
It follows that in a namespace-well-formed document:
All element and attribute names contain either zero or one colon;
No entity names, processing instruction targets, or notation names contain any colons.
You can use a trick that is to declare "SomeName" as a namespace as it is suggested in this question: xml schema validation error "prefix is not bound".
On the other hand, "Extensible Markup Language" Specification state that:
Note:
The Namespaces in XML Recommendation [XML Names] assigns a meaning to
names containing colon characters. Therefore, authors should not use
the colon in XML names except for namespace purposes, but XML
processors must accept the colon as a name character.
If you change the parser you can get what you want:
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
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;
import org.w3c.dom.Element;
public class CreateXmlFileDemo {
public static void main(String[] args) {
try {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.newDocument();
Element rootElement = doc.createElement("SomeName:enable");
doc.appendChild(rootElement);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult consoleResult = new StreamResult(System.out);
transformer.transform(source, consoleResult);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Reference: https://www.w3.org/TR/REC-xml-names/

update the input XML file with namespace in JAVA using DOM parser

How to update the XML node from <ns0:Request> to <ns1:Request xmlns:ns1="with some URL">
in Java using DOM parser.
I do not want to use the replaceAll() method.
You can do the following using the Node.replaceChild(Node, Node) method:
http://docs.oracle.com/javase/7/docs/api/org/w3c/dom/Node.html#replaceChild%28org.w3c.dom.Node,%20org.w3c.dom.Node%29
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class Demo {
public static void main(String[] args) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
// Create original document
Document document = db.newDocument();
Element root = document.createElementNS("urn:FOO", "ns0:Root");
document.appendChild(root);
Element request = document.createElementNS("urn:FOO", "ns0:Request");
root.appendChild(request);
// Create new Request element.
Element newRequest = document.createElementNS("urn:BAR", "ns1:Request");
// Replace Request element
root.replaceChild(newRequest, request);
// Output the new document
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(System.out);
t.transform(source, result);
}
}

Printing XML in Java without the xml file tag

Is there a way to print the XML content without the XML header tag in Java?
For example if I have an XML like this:
<?xml version='1.0' encoding='UTF-8'?>
<rootElement>
<childElement>Text</childElement>
</rootElement>
I just want to print
<rootElement>
<childElement>Text</childElement>
</rootElement>
This is very similar to what I am doing so far:
http://sacrosanctblood.blogspot.com/2008/07/convert-xml-file-to-xml-string-in-java.html
I cannot give out the exact source code but the above link example should give you some idea. Here's that code with imports:
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Text;
public String convertXMLFileToString(String fileName)
{
try{
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
InputStream inputStream = new FileInputStream(new File(fileName));
org.w3c.dom.Document doc = documentBuilderFactory.newDocumentBuilder().parse(inputStream);
StringWriter stw = new StringWriter();
Transformer serializer = TransformerFactory.newInstance().newTransformer();
serializer.transform(new DOMSource(doc), new StreamResult(stw));
return stw.toString();
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
Transformer serializer = TransformerFactory.newInstance().newTransformer();
serializer.setOutputProperty("omit-xml-declaration", "yes");
serializer.transform(new DOMSource(doc), new StreamResult(stw));
Good and old XSL ;).

inserting xml:space='preserve' in the DOM

Java, Xerces 2.9.1
insertHere.setAttributeNS(XMLConstants.XML_NS_URI, "xml:space", "preserve");
and
insertHere.setAttributeNS(XMLConstants.XML_NS_URI, "space", "preserve")
both end up with an attribute of just space='preserve', no XML prefix.
insertHere.setAttribute( "xml:space", "preserve")
works, but it seems somehow wrong. Am I missing anything?
EDIT
I checked.
I read a template document in with setNamespaceAware turned on.
I then use the following to make a copy of it, and then I start inserting new elements.
public static Document copyDocument(Document input) {
DocumentType oldDocType = input.getDoctype();
DocumentType newDocType = null;
Document newDoc;
String oldNamespaceUri = input.getDocumentElement().getNamespaceURI();
if (oldDocType != null) {
// cloning doctypes is 'implementation dependent'
String oldDocTypeName = oldDocType.getName();
newDocType = input.getImplementation().createDocumentType(oldDocTypeName,
oldDocType.getPublicId(),
oldDocType.getSystemId());
newDoc = input.getImplementation().createDocument(oldNamespaceUri, oldDocTypeName,
newDocType);
} else {
newDoc = input.getImplementation().createDocument(oldNamespaceUri,
input.getDocumentElement().getNodeName(),
null);
}
Element newDocElement = (Element)newDoc.importNode(input.getDocumentElement(), true);
newDoc.replaceChild(newDocElement, newDoc.getDocumentElement());
return newDoc;
}
When I run the following code:
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
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;
import org.w3c.dom.Element;
public class Demo {
public static void main(String[] args) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.newDocument();
Element rootElement = document.createElement("root");
document.appendChild(rootElement);
rootElement.setAttributeNS(XMLConstants.XML_NS_URI, "space", "preserve");
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
t.transform(new DOMSource(document), new StreamResult(System.out));
}
}
I get the following output:
<root xml:space="preserve"/>
How are you building your document?

xsl cdata-section-elements output property with Xalan-J

I'm trying to output a CDATA section in the result of XSLT using Xalan 2.7.1. I have applied this XSL to the XML in a tool and the result contains CDATA. In the method below, no CDATA is in the result and no exception is thrown. I feel like I'm missing something here.
test.xml
<?xml version="1.0" encoding="UTF-8"?>
<parentelem>
<childelem>Test text</childelem>
</parentelem>
test.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" cdata-section-elements="newchildelem" />
<xsl:template match="/">
<parentelem>
<newchildelem><xsl:value-of select="/parentelem/childelem" /></newchildelem>
</parentelem>
</xsl:template>
</xsl:stylesheet>
Transform.java
import java.io.FileReader;
import java.io.StringWriter;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stax.StAXResult;
import javax.xml.transform.stax.StAXSource;
public class Transform {
public static void main (String[] args){
try {
XMLStreamReader xmlReader = XMLInputFactory.newInstance().createXMLStreamReader(
new FileReader("test.xml"));
XMLStreamReader xslReader = XMLInputFactory.newInstance().createXMLStreamReader(
new FileReader("test.xsl"));
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Source xslSource = new StAXSource(xslReader);
Source xmlSource = new StAXSource(xmlReader);
Transformer transf = transformerFactory.newTransformer(xslSource);
StringWriter xmlString = new StringWriter();
XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(
xmlString);
Result transformedXml = new StAXResult(xmlWriter);
transf.transform(xmlSource, transformedXml);
xmlWriter.flush();
System.out.println(xmlString.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
console output
<?xml version="1.0" encoding="UTF-8"?><parentelem><newchildelem>Test text</newchildelem></parentelem>
Are you saying you want to output the CDATA as part of the element?
<newchildelem><xsl:value-of select="/parentelem/childelem" /></newchildelem>
with
<newchildelem><xsl:text><![CDATA[
</xsl:text><xsl:value-of select="/parentelem/childelem" /><xsl:text>]]&gt</xsl:text></newchildelem>
or some other form, but with the escaped characters to omit
<newchildelem><![CDATA[Test text]]></newchildelem>
or am I misunderstanding the question perhaps?
It works for me, with Xalan 2.7.1, not sure why it doesn't work for you.
I simplified the code fragment, but I don't think there's any functional difference, but try it anyway:
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.xalan.Version;
public class Transform {
public static void main(String[] args) throws Exception {
System.out.println(Version.getVersion());
Source xslSource = new StreamSource(Transform.class.getResourceAsStream("test.xsl"));
Source xmlSource = new StreamSource(Transform.class.getResourceAsStream("test.xml"));
Transformer transf = TransformerFactory.newInstance().newTransformer(xslSource);
StreamResult transformedXml = new StreamResult(System.out);
transf.transform(xmlSource, transformedXml);
}
}
Output is:
Xalan Java 2.4.1
<?xml version="1.0" encoding="UTF-8"?>
<parentelem><newchildelem><![CDATA[Test text]]></newchildelem></parentelem>
What is odd is that Xalan's Version.getVersion() returns 2.4.1, not 2.7.1, and I'm definitely using 2.7.1 here.
1) I guess it turns out i haven't been using Xalan 2.7.1. The code in skaffman's answer made me think to check Version.getVersion() and the signature is "XL TXE Java 1.0.7". This appears to be the default when using IBM java [IBM J9 VM (build 2.4, J2RE 1.6.0)].
2) I switched from using StAXSource and StAXResult to using StreamSource and StreamResult and it works fine (like in skaffman's answer). Specifically the change from StAXResult to StreamResult is what worked. Using StAXSource with StreamResult works too.

Categories