How to insert a schema into an XML document through the DOM

I am creating an XML document with JAXP and am looking for a way to insert a schema. My application is currently creating:

<?xml version="1.0" encoding="UTF-8"?> <root> ... </root> 

But I need:

 <?xml version="1.0" encoding="UTF-8"?> <root xmlns="namespaceURL" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:schemaLocation="namespaceURL pathToMySchema.xsd"> ... </root> 

My code is:

 StreamResult result = new StreamResult(writer); Document doc = getDocument(); Transformer trans = transfac.newTransformer(); trans.setOutputProperty(OutputKeys.INDENT, "yes"); trans.setOutputProperty(OutputKeys.METHOD, "xml"); trans.setOutputProperty(OutputKeys.VERSION, "1.0"); trans.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); DOMSource source = new DOMSource(depl.getAsElement(doc)); trans.transform(source, result); 

Thank you for your time,
Kasten

+6
dom xsd jaxp
source share
3 answers

The namespace nodes of the XML data do not actually read the namespace nodes from the parent element, but each element has its own namespace nodes. So just adding a new default namespace to the root element does not work, but leads to a document like this

 <root xmlns="namespaceURL"> <child xmlns=""/> ... </root> 

Note the appearance of the default empty namespace xmlns="" for the child element (s). What you need to do is change the namespace of each node or create a new document with the required default namespace and copy the contents, elements and attribute names, etc. Old document to new. This can be done by recursively traversing the source document. With a Java DOM implementation, this can be time consuming, I heard. One short cut can be reading a document using the DOM with a namespace, and then adding a new default namespace as an attribute. Another solution is to change the namespace using the XSLT transform, which seems quite appropriate in this case, since you are actually already generating the result using the XSLT transform.

Use this XSLT stylesheet to add a new default namespace and layout to the root element. This style sheet retains the old namespace, but adds all the elements to the new default namespace if they were not previously in the namespace.

 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <!-- Template to add a default namespace to a document --> <!-- Elements without a namespace are "moved" to default namespace --> <!-- Elements with a namespace are copied as such --> <!-- string for default namespace uri and schema location --> <xsl:variable name="ns" select="'namespaceURL'"/> <xsl:variable name="schemaLoc" select="'namespaceURL pathToMySchema.xsd'"/> <!-- template for root element --> <!-- adds default namespace and schema location --> <xsl:template match="/*" priority="1"> <xsl:element name="{local-name()}" namespace="{$ns}"> <xsl:attribute name="xsi:schemaLocation" namespace="http://www.w3.org/2001/XMLSchema-instance"> <xsl:value-of select="$schemaLoc"/> </xsl:attribute> <xsl:apply-templates select="@* | node()"/> </xsl:element> </xsl:template> <!--template for elements without a namespace --> <xsl:template match="*[namespace-uri() = '']"> <xsl:element name="{local-name()}" namespace="{$ns}"> <xsl:apply-templates select="@* | node()"/> </xsl:element> </xsl:template> <!--template for elements with a namespace --> <xsl:template match="*[not(namespace-uri() = '')]"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <!--template to copy attributes, text, PIs and comments --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet> 

Instead of creating a transformer with

 Transformer trans = transfac.newTransformer(); 

(which creates a stylesheet that performs the same conversion), create an XSLT input source and give it as parameter newTransformer()

 javax.xml.transform.Source xsltSource = new javax.xml.transform.stream.StreamSource(xsltFile); Transformer trans = transFact.newTransformer(xsltSource); 

where xsltFile is a File object pointing to this XSLT file.

Set the output properties as you like and call transform() , as in your code example. The result should be what you need, but I have not tested this in Java . This XSLT file is checked for some trivial cases, and there is sample input and output at the end of this answer.

Some minor notes:

  • In this process, the original document object is not changed. The new default namespace appears only at the output of the transform() method.
  • The namespace prefix for the namespace of the schema instance is usually xsi: rather than xs: as in your code example ( xs: used in schema definitions (as well as xsd: ).

Sample input and output for the XSLT stylesheet shown above

Input:

 <root> <child>text</child> <child attribute="attr-value"/> <?pi-target pi-content?> <nsx:ns-child xmlns:nsx="ns1x"> <no-ns-child>text</no-ns-child> <!-- comment --> <nsx:ns-child nsx:ns-attribute="nsx-attr-value">text</nsx:ns-child> </nsx:ns-child> <defns-child xmlns="default-ns"> <def-child attr="val">text</def-child> <child xmlns=""/> </defns-child> <child>text</child> </root> 

Output:

 <?xml version="1.0" encoding="UTF-8"?> <root xmlns="namespaceURL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="namespaceURL pathToMySchema.xsd"> <child>text</child> <child attribute="attr-value"/> <?pi-target pi-content?> <nsx:ns-child xmlns:nsx="ns1x"> <no-ns-child>text</no-ns-child> <!-- comment --> <nsx:ns-child nsx:ns-attribute="nsx-attr-value">text</nsx:ns-child> </nsx:ns-child> <defns-child xmlns="default-ns"> <def-child attr="val">text</def-child> <child xmlns="namespaceURL"/> </defns-child> <child>text</child> </root> 
+6
source share

You can add namespaces to the root directory when creating the document.

 String NS_URL = "namespaceURL"; doc = builder.newDocument(); Element root = doc.createElementNS(NS_URL, "root"); root.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "xs:schemaLocation", NS_URL + " pathToMySchema.xsd"); doc.appendChild(root); 

Then for each element added to the doc instead of createElement (), use createElementNS ()

 doc.createElementNS(NS_URL, name); 

This leads to what you were looking for.

 <root xmlns="namespaceURL" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:schemaLocation="namespaceURL pathToMySchema.xsd" > 
+4
source share

Here's how to give a hint to the parser to solve your problem: http://bytes.com/topic/java/answers/16892-xerces-how-perfrom-schema-validations-without-using-xsi-schemalocation

This happens as follows:

 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); dbf.setValidating(true); dbf.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema"); dbf.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLocation", "http://www.example.com/Report.xsd"); 

Here is an example check with some source code. It can help you. http://www.ibm.com/developerworks/xml/library/x-tipvalschm/

(If things get worse, you can always search and replace. I know this is not an ideal solution, but javax.xml.transform.OutputKeys does not seem to have a member associated with the schemalocation attribute.)

+1
source share

All Articles