Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4981389

Cannot correctly serialize namespaced DOM trees in either JDK 1.4 or 1.5

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P3 P3
    • jwsdp-1.4
    • 1.4.2_04
    • xml

      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&#39;&gt;&lt;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

            bhamehta Bhakti Mehta (Inactive)
            jglick Jesse Glick (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: