-
Bug
-
Resolution: Fixed
-
P3
-
1.4.2_04
-
1.4
-
x86
-
linux_redhat_7.2
There seem to be problems using JAXP to serialize XML documents with namespaces in both JDK 1.4 and 1.5, at least when the documents are created in memory using DOM; some problems disappear if the document originates from an XML parser.
Below I am including a sample class which tries various techniques to save a document to a stream: the identity transformation; the org.apache.xml.serialize package included in Xerces; and DOM 3 LS. Run as is on JDK 1.4 and 1.5. On JDK 1.3, with no JAXP, I add to the classpath xerces.jar, xalan.jar, and xml-apis.jar, all built from CVS HEAD sources current as of 2004-01-21 on xml.apache.org.
On JDK 1.3, there are some problems, which I have reported in
http://nagoya.apache.org/bugzilla/show_bug.cgi?id=26319
Specifically, Xalan does not correctly serialize namespaced attributes; Xerces does. Xalan fails to use xmlns="" to unset the default namespace for a nested element; Xerces does it correctly (at least if the element was made using the namespace-aware DOM 2 method).
Now, on to the Sun JDKs. On 1.4, the output is badly broken; it is apparently not aware of namespaces in any way. (You can manually add attributes named e.g. 'xmlns:prefix' to the DOM tree and they are sent through to the output.)
On 1.5, some bugs have been fixed; at least elements get namespace declarations, and attributes whose QName had a prefix do too. Several bugs remain however:
1. The default namespace declaration on the nested element <bar> is gratuitous, since it does not change the default namespace declared on <foo>. This is just stylistic, but Xerces does a better job.
2. The attribute {urn:test3}key on <bar> is not given a namespace prefix and declaration, so it is incorrectly written as if it had no namespace. Xerces correctly adds a generated prefix and a declaration for that prefix.
3. <quux2> is written with no override of the default namespace declaration on <foo>, so it is incorrectly written as if it had the namespace "urn:test1", when it should have none. Xerces correctly overrides the namespace to be null.
Note that 1.5 actually ships with a version of Xerces which behaves correctly, and you can access it through the DOM 3 LS interface. But the serializer used by the version of Xalan shipped with 1.5 apparently uses some other, buggier, algorithm.
Test case:
$ cat /home/jglick/nbdev/sampledir/TryJaxpSerialization.java; echo ---------------; /space/jdk1.3/bin/javac -classpath /space/src/xmlcommons/java/external/build/xml-apis.jar ~/nbdev/sampledir/TryJaxpSerialization.java; /space/jdk1.3/bin/java -showversion -cp /home/jglick/nbdev/sampledir:/space/src/xalan/build/xalan.jar:/space/src/xerces/build/xercesImpl.jar:/space/src/xmlcommons/java/external/build/xml-apis.jar TryJaxpSerialization; /space/jdk1.4/bin/java -showversion -cp /home/jglick/nbdev/sampledir TryJaxpSerialization; /space/jdk1.5/bin/java -showversion -cp /home/jglick/nbdev/sampledir TryJaxpSerialization
import java.io.OutputStream;
import java.io.StringReader;
import java.lang.reflect.Method;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
public class TryJaxpSerialization {
public static void main(String[] args) throws Exception {
DOMImplementation dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().getDOMImplementation();
Document doc = dom.createDocument("urn:test1", "foo", null);
Element foo = doc.getDocumentElement();
Element bar = doc.createElementNS("urn:test1", "bar");
bar.setAttributeNS("urn:test3", "key", "val");
bar.setAttributeNS("urn:test4", "t4:key2", "val2");
Element baz = doc.createElementNS("urn:test2", "baz");
Element quux1 = doc.createElement("quux1");
Element quux2 = doc.createElementNS(null, "quux2");
foo.appendChild(bar);
foo.appendChild(baz);
foo.appendChild(quux1);
foo.appendChild(quux2);
System.out.println("------%<------ JAXP implicit identity transformation wrote:");
Transformer t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
Source source = new DOMSource(doc);
Result result = new StreamResult(System.out);
t.transform(source, result);
/* Always seems to give the same result, so skipping:
System.out.println("------%<------ JAXP explicit identity transformation wrote:");
Source idsource = new StreamSource(new StringReader("<stylesheet version='1.0' xmlns='http://www.w3.org/1999/XSL/Transform'><output method='xml'/><template match='/'><copy-of select='/'/></template></stylesheet>"));
t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
source = new DOMSource(doc);
result = new StreamResult(System.out);
t.transform(source, result);
*/
try {
Class xmlser = Class.forName("org.apache.xml.serialize.XMLSerializer");
Object serializer = xmlser.newInstance();
Method setNamespaces = xmlser.getMethod("setNamespaces", new Class[] {Boolean.TYPE});
setNamespaces.invoke(serializer, new Object[] {Boolean.TRUE});
Class outfmt = Class.forName("org.apache.xml.serialize.OutputFormat");
Object format = outfmt.newInstance();
Method setIndenting = outfmt.getMethod("setIndenting", new Class[] {Boolean.TYPE});
setIndenting.invoke(format, new Object[] {Boolean.TRUE});
Method setOutputFormat = xmlser.getMethod("setOutputFormat", new Class[] {outfmt});
setOutputFormat.invoke(serializer, new Object[] {format});
Method setOutputByteStream = xmlser.getMethod("setOutputByteStream", new Class[] {OutputStream.class});
setOutputByteStream.invoke(serializer, new Object[] {System.out});
Method serialize = xmlser.getMethod("serialize", new Class[] {Document.class});
System.out.println("------%<------ XMLSerializer wrote:");
serialize.invoke(serializer, new Object[] {doc});
} catch (ClassNotFoundException e) {
System.out.println("------%<------ no XMLSerializer available");
}
try {
Class domimplls = Class.forName("org.w3c.dom.ls.DOMImplementationLS");
if (domimplls.isInstance(dom)) {
Method createLSSerializer = domimplls.getMethod("createLSSerializer", null);
Object serializer = createLSSerializer.invoke(dom, null);
Method createLSOutput = domimplls.getMethod("createLSOutput", null);
Object output = createLSOutput.invoke(dom, null);
Method setByteStream = output.getClass().getMethod("setByteStream", new Class[] {OutputStream.class});
setByteStream.invoke(output, new Object[] {System.out});
Method write = serializer.getClass().getMethod("write", new Class[] {Node.class, Class.forName("org.w3c.dom.ls.LSOutput")});
System.out.println("------%<------ DOM 3 LS wrote:");
write.invoke(serializer, new Object[] {doc, output});
System.out.println();
}
} catch (ClassNotFoundException e) {
System.out.println("------%<------ no DOM 3 LS available");
}
System.out.println("------%<------");
}
}
---------------
java version "1.3.1_07"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1_07-b02)
Java HotSpot(TM) Client VM (build 1.3.1_07-b02, mixed mode)
------%<------ JAXP implicit identity transformation wrote:
<?xml version="1.0" encoding="UTF-8"?>
<foo xmlns="urn:test1">
<bar key="val" t4:key2="val2"/>
<baz xmlns="urn:test2"/>
<quux1/>
<quux2/>
</foo>
------%<------ XMLSerializer wrote:
<?xml version="1.0" encoding="UTF-8"?>
<foo xmlns="urn:test1">
<bar xmlns:NS1="urn:test3" NS1:key="val" xmlns:t4="urn:test4" t4:key2="val2"/>
<baz xmlns="urn:test2"/>
<quux1/>
<quux2 xmlns=""/>
</foo>
------%<------ DOM 3 LS wrote:
<?xml version="1.0" encoding="UTF-8"?>
<foo xmlns="urn:test1"><bar xmlns:NS1="urn:test3" NS1:key="val" xmlns:t4="urn:test4" t4:key2="val2"/><baz xmlns="urn:test2"/><quux1/><quux2 xmlns=""/></foo>
------%<------
java version "1.4.2_04"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_04-b02)
Java HotSpot(TM) Client VM (build 1.4.2_04-b02, mixed mode)
------%<------ JAXP implicit identity transformation wrote:
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<bar key="val" t4:key2="val2"/>
<baz/>
<quux1/>
<quux2/>
</foo>
------%<------ no XMLSerializer available
------%<------ no DOM 3 LS available
------%<------
java version "1.5.0-beta2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta2-b33)
Java HotSpot(TM) Client VM (build 1.5.0-beta2-b33, mixed mode)
------%<------ JAXP implicit identity transformation wrote:
<?xml version="1.0" encoding="UTF-8"?>
<foo xmlns="urn:test1">
<bar xmlns="urn:test1" key="val" xmlns:t4="urn:test4" t4:key2="val2"/>
<baz xmlns="urn:test2"/>
<quux1/>
<quux2/>
</foo>
------%<------ no XMLSerializer available
------%<------ DOM 3 LS wrote:
<?xml version="1.0" encoding="UTF-8"?>
<foo xmlns="urn:test1"><bar xmlns:NS1="urn:test3" NS1:key="val" xmlns:t4="urn:test4" t4:key2="val2"/><baz xmlns="urn:test2"/><quux1/><quux2 xmlns=""/></foo>
------%<------
###@###.### 2004-01-21
###@###.### 2004-01-21
Below I am including a sample class which tries various techniques to save a document to a stream: the identity transformation; the org.apache.xml.serialize package included in Xerces; and DOM 3 LS. Run as is on JDK 1.4 and 1.5. On JDK 1.3, with no JAXP, I add to the classpath xerces.jar, xalan.jar, and xml-apis.jar, all built from CVS HEAD sources current as of 2004-01-21 on xml.apache.org.
On JDK 1.3, there are some problems, which I have reported in
http://nagoya.apache.org/bugzilla/show_bug.cgi?id=26319
Specifically, Xalan does not correctly serialize namespaced attributes; Xerces does. Xalan fails to use xmlns="" to unset the default namespace for a nested element; Xerces does it correctly (at least if the element was made using the namespace-aware DOM 2 method).
Now, on to the Sun JDKs. On 1.4, the output is badly broken; it is apparently not aware of namespaces in any way. (You can manually add attributes named e.g. 'xmlns:prefix' to the DOM tree and they are sent through to the output.)
On 1.5, some bugs have been fixed; at least elements get namespace declarations, and attributes whose QName had a prefix do too. Several bugs remain however:
1. The default namespace declaration on the nested element <bar> is gratuitous, since it does not change the default namespace declared on <foo>. This is just stylistic, but Xerces does a better job.
2. The attribute {urn:test3}key on <bar> is not given a namespace prefix and declaration, so it is incorrectly written as if it had no namespace. Xerces correctly adds a generated prefix and a declaration for that prefix.
3. <quux2> is written with no override of the default namespace declaration on <foo>, so it is incorrectly written as if it had the namespace "urn:test1", when it should have none. Xerces correctly overrides the namespace to be null.
Note that 1.5 actually ships with a version of Xerces which behaves correctly, and you can access it through the DOM 3 LS interface. But the serializer used by the version of Xalan shipped with 1.5 apparently uses some other, buggier, algorithm.
Test case:
$ cat /home/jglick/nbdev/sampledir/TryJaxpSerialization.java; echo ---------------; /space/jdk1.3/bin/javac -classpath /space/src/xmlcommons/java/external/build/xml-apis.jar ~/nbdev/sampledir/TryJaxpSerialization.java; /space/jdk1.3/bin/java -showversion -cp /home/jglick/nbdev/sampledir:/space/src/xalan/build/xalan.jar:/space/src/xerces/build/xercesImpl.jar:/space/src/xmlcommons/java/external/build/xml-apis.jar TryJaxpSerialization; /space/jdk1.4/bin/java -showversion -cp /home/jglick/nbdev/sampledir TryJaxpSerialization; /space/jdk1.5/bin/java -showversion -cp /home/jglick/nbdev/sampledir TryJaxpSerialization
import java.io.OutputStream;
import java.io.StringReader;
import java.lang.reflect.Method;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
public class TryJaxpSerialization {
public static void main(String[] args) throws Exception {
DOMImplementation dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().getDOMImplementation();
Document doc = dom.createDocument("urn:test1", "foo", null);
Element foo = doc.getDocumentElement();
Element bar = doc.createElementNS("urn:test1", "bar");
bar.setAttributeNS("urn:test3", "key", "val");
bar.setAttributeNS("urn:test4", "t4:key2", "val2");
Element baz = doc.createElementNS("urn:test2", "baz");
Element quux1 = doc.createElement("quux1");
Element quux2 = doc.createElementNS(null, "quux2");
foo.appendChild(bar);
foo.appendChild(baz);
foo.appendChild(quux1);
foo.appendChild(quux2);
System.out.println("------%<------ JAXP implicit identity transformation wrote:");
Transformer t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
Source source = new DOMSource(doc);
Result result = new StreamResult(System.out);
t.transform(source, result);
/* Always seems to give the same result, so skipping:
System.out.println("------%<------ JAXP explicit identity transformation wrote:");
Source idsource = new StreamSource(new StringReader("<stylesheet version='1.0' xmlns='http://www.w3.org/1999/XSL/Transform'><output method='xml'/><template match='/'><copy-of select='/'/></template></stylesheet>"));
t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
source = new DOMSource(doc);
result = new StreamResult(System.out);
t.transform(source, result);
*/
try {
Class xmlser = Class.forName("org.apache.xml.serialize.XMLSerializer");
Object serializer = xmlser.newInstance();
Method setNamespaces = xmlser.getMethod("setNamespaces", new Class[] {Boolean.TYPE});
setNamespaces.invoke(serializer, new Object[] {Boolean.TRUE});
Class outfmt = Class.forName("org.apache.xml.serialize.OutputFormat");
Object format = outfmt.newInstance();
Method setIndenting = outfmt.getMethod("setIndenting", new Class[] {Boolean.TYPE});
setIndenting.invoke(format, new Object[] {Boolean.TRUE});
Method setOutputFormat = xmlser.getMethod("setOutputFormat", new Class[] {outfmt});
setOutputFormat.invoke(serializer, new Object[] {format});
Method setOutputByteStream = xmlser.getMethod("setOutputByteStream", new Class[] {OutputStream.class});
setOutputByteStream.invoke(serializer, new Object[] {System.out});
Method serialize = xmlser.getMethod("serialize", new Class[] {Document.class});
System.out.println("------%<------ XMLSerializer wrote:");
serialize.invoke(serializer, new Object[] {doc});
} catch (ClassNotFoundException e) {
System.out.println("------%<------ no XMLSerializer available");
}
try {
Class domimplls = Class.forName("org.w3c.dom.ls.DOMImplementationLS");
if (domimplls.isInstance(dom)) {
Method createLSSerializer = domimplls.getMethod("createLSSerializer", null);
Object serializer = createLSSerializer.invoke(dom, null);
Method createLSOutput = domimplls.getMethod("createLSOutput", null);
Object output = createLSOutput.invoke(dom, null);
Method setByteStream = output.getClass().getMethod("setByteStream", new Class[] {OutputStream.class});
setByteStream.invoke(output, new Object[] {System.out});
Method write = serializer.getClass().getMethod("write", new Class[] {Node.class, Class.forName("org.w3c.dom.ls.LSOutput")});
System.out.println("------%<------ DOM 3 LS wrote:");
write.invoke(serializer, new Object[] {doc, output});
System.out.println();
}
} catch (ClassNotFoundException e) {
System.out.println("------%<------ no DOM 3 LS available");
}
System.out.println("------%<------");
}
}
---------------
java version "1.3.1_07"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1_07-b02)
Java HotSpot(TM) Client VM (build 1.3.1_07-b02, mixed mode)
------%<------ JAXP implicit identity transformation wrote:
<?xml version="1.0" encoding="UTF-8"?>
<foo xmlns="urn:test1">
<bar key="val" t4:key2="val2"/>
<baz xmlns="urn:test2"/>
<quux1/>
<quux2/>
</foo>
------%<------ XMLSerializer wrote:
<?xml version="1.0" encoding="UTF-8"?>
<foo xmlns="urn:test1">
<bar xmlns:NS1="urn:test3" NS1:key="val" xmlns:t4="urn:test4" t4:key2="val2"/>
<baz xmlns="urn:test2"/>
<quux1/>
<quux2 xmlns=""/>
</foo>
------%<------ DOM 3 LS wrote:
<?xml version="1.0" encoding="UTF-8"?>
<foo xmlns="urn:test1"><bar xmlns:NS1="urn:test3" NS1:key="val" xmlns:t4="urn:test4" t4:key2="val2"/><baz xmlns="urn:test2"/><quux1/><quux2 xmlns=""/></foo>
------%<------
java version "1.4.2_04"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_04-b02)
Java HotSpot(TM) Client VM (build 1.4.2_04-b02, mixed mode)
------%<------ JAXP implicit identity transformation wrote:
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<bar key="val" t4:key2="val2"/>
<baz/>
<quux1/>
<quux2/>
</foo>
------%<------ no XMLSerializer available
------%<------ no DOM 3 LS available
------%<------
java version "1.5.0-beta2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta2-b33)
Java HotSpot(TM) Client VM (build 1.5.0-beta2-b33, mixed mode)
------%<------ JAXP implicit identity transformation wrote:
<?xml version="1.0" encoding="UTF-8"?>
<foo xmlns="urn:test1">
<bar xmlns="urn:test1" key="val" xmlns:t4="urn:test4" t4:key2="val2"/>
<baz xmlns="urn:test2"/>
<quux1/>
<quux2/>
</foo>
------%<------ no XMLSerializer available
------%<------ DOM 3 LS wrote:
<?xml version="1.0" encoding="UTF-8"?>
<foo xmlns="urn:test1"><bar xmlns:NS1="urn:test3" NS1:key="val" xmlns:t4="urn:test4" t4:key2="val2"/><baz xmlns="urn:test2"/><quux1/><quux2 xmlns=""/></foo>
------%<------
###@###.### 2004-01-21
###@###.### 2004-01-21
- relates to
-
JDK-6308026 Nested elements with namespaces not serialized correctly in JDK 1.4
-
- Closed
-