I need to generate a XML file that contains specific XML Declarations and DTD declarations as shown below:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE paymentService PUBLIC "-//CompanyName//DTD CompanyName PaymentService v2//EN"
"http://dtd.CompanyName.com/paymentService_v2.dtd">
The remaining XML to be generated also has a specific Elements and associated values.
I was wondering what would be the best way to generate this XML within my java class? Using String Buffer or DOM? Any suggestions with an example or sample code will be hugely appreciated.
Thanks
I would recommend using the Java DOM API. Dealing with XML or XHTML in String objects is notoriously time consuming and buggy, so try and use a propper parser like DOM whenever you have the option to.
The below code should add a doc type and your xml declaration using Java DOM. The <?xml... should be added to the top automatically when the DocumentBuilder creates your document.
// Create document
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.newDocument();
//Create doc type
DOMImplementation domImpl = doc.getImplementation();
DocumentType doctype = domImpl.createDocumentType("paymentService", "-//CompanyName//DTD CompanyName PaymentService v2//EN", "http://dtd.CompanyName.com/paymentService_v2.dtd");
doc.appendChild(doctype);
// Add root element
Element rootElement = doc.createElement("root");
doc.appendChild(rootElement);
The XML created by the above should look like this;
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<!DOCTYPE paymentService PUBLIC "-//CompanyName//DTD CompanyName PaymentService v2//EN" "http://dtd.CompanyName.com/paymentService_v2.dtd">
<root>
</root>
A great many of the methods used in the code above can throw a large number and variety of exceptions, so make sure your exception handling is up to scratch. I hope this helps.
Link to the Official DOM API Guide
Related
I've been trying to get XInclude working in my XML document and finally have it working in Oxygen XML, which I'm using to author the XML documents.
I then went to my app, written in Java, but it doesn't seem to support any form of XPointer resolution except using something like: element(/1/2).
This is, obviously, an awful scheme to have to use since every time the document is edited the XPointer needs changing to reflect the new position of the node in the XML!
The scheme I had working simply used xml:id in the target document:
<foo>
<bar xml:id="ABCD" />
</foo>
and then, in the other document:
<lorem>
<ipsum>
<xi:include href="target.xml" xpointer="ABCD" />
</ipsum>
</lorem>
Which I anticipate (and am getting in Oxygen) results in something like:
<lorem>
<ipsum>
<bar xml:id="ABCD" />
</ipsum>
</lorem>.
However, in Java it fails with:
Resource error reading file as XML (href='data/target.xml'). Reason:
XPointer resolution unsuccessful.
If, however, I change the include tag to use
xpointer="element(/1/1)"
then it works very nicely - but, as I've said, that's a very poor solution.
I'm simply using the implementations that are included with the Java runtime (1.8).
Here's the code I'm using:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setXIncludeAware(true);
Source resultSource = new
StreamSource(Gdx.files.internal("data/result.xsd").read());
Source targetSource = new
StreamSource(Gdx.files.internal("data/target.xsd").read());
Source[] schemaFiles = {targetSource, resultSource};
schema =
SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema")
.newSchema(schemaFiles);
factory.setSchema(schema);
builder = factory.newDocumentBuilder();
itemDoc = builder.parse(new
InputSource(Gdx.files.internal("data/result.xml").read()));
According to Apache Xerces's docs on XInclude (which is used internally for XML parsing by Java)
for shorthand pointers and element() XPointers, currently only DTD-determined IDs are supported.
This means that you need to put markup declarations such as the following into your target.xml file (telling the XML parser that the id attribute is to be treated as attribute with ID semantics, and telling XInclude to interpret "bare" XPointers as ID references):
<!DOCTYPE foo [
<!ATTLIST bar id ID #IMPLIED>
]>
<foo>
<bar id="ABCD"/>
</foo>
If you now use the following document as your source XML (which you've named result.xml in your example code, and which I've edited to contain an XInclude namespace URI binding for xi)
<lorem xmlns:xi="http://www.w3.org/2001/XInclude">
<ipsum>
<xi:include href="target.xml" xpointer="ABCD"/>
</ipsum>
</lorem>
then Xerces will build up a Document where XInclude processing has been performed as desired (where i've put your example data into the target.xml file in the same directory as the result.xml file):
<lorem xmlns:xi="http://www.w3.org/2001/XInclude">
<ipsum>
<bar id="ABCD" xml:base="target.xml"/>
</ipsum>
</lorem>
The Java code I've used to produce the document is simplified from your example and doesn't contain third-party libs:
import java.io.*;
import javax.xml.*;
import javax.xml.parsers.*;
import javax.xml.validation.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
import javax.xml.transform.dom.*;
import org.w3c.dom.*;
public class t {
public static void main(String[] args) {
try {
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setXIncludeAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document itemDoc = builder.parse(new File("result.xml"));
System.out.println(serialize(itemDoc));
}
catch (Exception ex) {
ex.printStackTrace();
}
}
static String serialize(Document doc) throws Exception {
Transformer transformer =
TransformerFactory.newInstance().newTransformer();
StreamResult result = new StreamResult(new StringWriter());
DOMSource source = new DOMSource(doc);
transformer.transform(source, result);
return result.getWriter().toString();
}
}
Seeing as you also use XML Schema validation, I'd also like to point out the potential interaction of XInclude with XML Schema as eg. discussed in XInclude Schema/Namespace Validation?, and also a potential alternative discussed in Duplicate some parts of XML without rewriting them .
In one part of my app, I'm using this code to read a RSS feed:
DocumentBuilder builder = factory.newDocumentBuilder();
Document dom = builder.parse(this.url.openConnection().getInputStream());
Element root = dom.getDocumentElement();
NodeList items = root.getElementsByTagName("item");
for (int i=0;i<items.getLength();i++){...
The problem is that one of the feeds that I want to read starts whith a whitespace just before the <?xml just like that
<?xml version="1.0" encoding="utf-8"?>
<!-- generator="Joomla! - Open Source Content Management" -->
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
When my app tries to read this feed throw the following error:
org.xml.sax.SAXParseException: processing instructions must not start with xml (position:unknown #1:2 in java.io.InputStreamReader#605667c)
Now my doubt is: how can I avoid this error?
Thanks.
If you are sure with the structure of response you can use skip() method of InputStream. Find below the code snippet.
Document dom = builder.parse(this.url.openConnection().getInputStream().skip(1));
Element root = dom.getDocumentElement();
Else convert the InputStream to String. You can use Apache IOUtils.
Process the String and then parse the xml.
I'm having a problem generating a document with signed nodes. I have a bunch of signed XMLs, in text format. They signature is valid, I've tested it with xmlsec1. I have to load all the XMLs and put them in another XML document, for sending it to another service.
So, first of all I create the container document ("sobre" is a local variable, a JAXB root element):
JAXBContext context = JAXBContext.newInstance("org.importe.test");
StringWriter writer = new StringWriter();
Marshaller marshaller = context.createMarshaller();
marshaller.marshal(sobre, writer);
String xml = writer.toString();
Document doc = loadXMLFromString(xml);
and then I add the XMLs to the container:
for (String cfexml : cfexmls) {
Document cfe = loadXMLFromString(cfexml);
Node newNode = doc.importNode(cfe.getDocumentElement(), true);
doc.getElementsByTagName("EnvioCFE").item(0).appendChild(newNode);
}
finally I get the xml from the container document:
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
StringWriter outputWriter = new StringWriter();
trans.transform(new DOMSource(doc), new StreamResult(outputWriter));
String signedxml = outputWriter.toString();
The point is that I'm getting the child node modified, without namespaces, and so the signature validation of the extract node fails. Here is an excerpt of the XML that I've to add as a child node:
<?xml version="1.0" encoding="UTF-8"?>
<CFE xmlns="http://org.importe.test" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" xmlns:ns3="http://www.w3.org/2001/04/xmlenc#" version="1.0">
<data>
[...]
</data>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
[...]
</Signature>
</CFE>
but when the node is imported in the container document it get modified (actually I've noticed that it loose namespace declarations):
<?xml version="1.0" encoding="UTF-8"?>
<EnvioCFE xmlns="http://org.importe.test" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" xmlns:ns3="http://www.w3.org/2001/04/xmlenc#" version="1.0">
<Header version="1.0">
</Header>
<CFE version="1.0">
<data>
[...]
</data>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
[...]
</Signature>
</CFE>
</EnvioCFE>
How can I add a node to the container keeping the namespace declaration? More generally can I add it being sure it's added "as is", without any modifications?
(I'm using glassfish 4 and Java 7)
Thank you
EDIT:
This is the code of loadXMLFromString:
public Document loadXMLFromString(String xml) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(new InputSource(new StringReader(xml)));
}
Ok, I've done research and I've found the problem:
if I use xalan-2.7.1.jar, xercesImpl-2.9.1.jar, xml-apis-1.3.04.jar and xmlsec-1.5.6.jar for DOM and XML security implementation, it works as I expect: adding a node does not modify the contents, so the digital signature is valid;
if I use the standard java libraries not only adding a node change the node itself, but sometimes the digital signature doesn't pass the xmlsec1 check, I imagine for a problem in the transformation from DOM to String.
I hope this can help someone, anyway I don't know why it happens, I'm sure I'm not doing weird things, but I've no time now to going deeply.
Actually I'm stuck to find how tell glassfish to load the DOM implementation I want, or to set a priority: I've found no way to say "load xerces instead of standard lib".
final SAXBuilder builder = new SAXBuilder();
final Document xml = builder.build( file );
final Element root = (Element) xml.getRootElement();
Element child = root.getChild("Header", Namespace.getNamespace("http://www.editeur.org/icedis/claims"));
final XPathContext ctx = XPathContext.create(root);
This is the code I have. XPathContext is just wrapper around JXPathContext. As you see, 'child' variable using construction with Namespace and it won't work if I remove it.
But now I'm trying to get value from XML with XPath like this
ctx.getValue("/Header/SentDateTime")
And, ofcourse it always return null. It seems that solution is simple, but I can't find it in the internet. If I delete xmlns from XML file, everything will work, but this isn't an option. So how should I work with this xml?
Also, short version of XML file:
<?xml version="1.0" encoding="UTF-8"?>
<ICEDISClaimMessage version="0.1" xmlns="http://www.editeur.org/icedis/claims">
<Header>
<Sender>
<SenderIdentifier>
<SenderIDType>06</SenderIDType>
<IDValue>1234567890128</IDValue>
</SenderIdentifier>
<SenderName>Apogee Agency</SenderName>
</Sender>
<Addressee>
<AddresseeIdentifier>
<AddresseeIDType>06</AddresseeIDType>
<IDValue>2345678901237</IDValue>
</AddresseeIdentifier>
</Addressee>
<MessageNumber>111</MessageNumber>
<SentDateTime>20101001</SentDateTime>
<MessageNote>Sample file 1: a claim made by an agent to a publisher for a journal issue not yet delivered</MessageNote>
</Header>
</<ICEDISClaimMessage>
You need to use an XPath expression with a namespace context. It would have to look something like this:
final XPathContext ctx = XPathContext.create(root);
ctx.addNamespaceMapping("ns", "http://www.editeur.org/icedis/claims");
... = ctx.getValue("/ns:Header/ns:SentDateTime");
NB: I don't know whether JXPath supports namespaces, and if it does, how it is set up.
And, by the way, it looks like you are using JDOM (SAXBuilder). In that case I would recommend using the JDOM built in XPath functions instead of building a wrapper.
I have a service that gives some car information in an xml format.
<?xml version="1.0" encoding='UTF-8'?>
<cars>
<car>
<id>5</id>
<name>qwer</name>
</car>
<car>
<id>6</id>
<name>qwert</name>
</car>
</cars>
Now the problem that I'm having is that my
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
Document doc = docBuilder.parse(xml);
Sometimes throws a SAXException (sometimes it works just fine, but when I reboot the server (still in development) I sometimes keep getting it) with as cause SAXException: unexpected end of document.
But when I place a bufferreader there to see what it's receiving and I copy the value into an xml document and I open it in firefox/ie it looks just fine.
An XML document must have one, and only one, root element.
You should have a <cars> element (or similar) wrapping your group of <car>s.
The error message doesn't make sense though - since you have unexpected content after what should be the end of the document.
You get this exception because the example you entered is a valid XML fragment (as a consequence readable by Firefox), but an invalid XML document, as it has more than one root node, which is forbidden by XML rules.
Try to create one XML document for each <car> tag, and SAX will be fine.