AbstractDOMOutputProcessor doesn't explicitly set namespace of xmlns attributes #115

Closed
petergeneric opened this Issue Apr 12, 2013 · 3 comments

Projects

None yet

2 participants

@petergeneric
Contributor

Hello,

The default implementation of DOMOutputProcessor generates xmlns attributes without setting the namespace. On the JAXP in Oracle Java 1.7 this means the generated Attrs have a null Namespace URI. This is causing us problems when serialising these Elements as part of a JAXB object (using EclipseLink MOXy - the Metro implementation is able to cope with it).

I'm not entirely sure if this is strictly a bug in JDOM or if the fault lies with JAXP for not inferring the Namespace URI on an element prefixed with "xmlns" - but it does mean the Documents coming from DOMOutputter don't look the same as a document coming from the built-in DocumentBuilderFactory.

Here's a JUnit test to reproduce the issue.

import org.jdom2.Element;
import org.jdom2.JDOMConstants;
import org.jdom2.input.DOMBuilder;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.DOMOutputter;
import org.junit.Test;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;

import static org.junit.Assert.assertEquals;

public class DOMOutputterNamespaceTest
{
    /**
     * The XML document to test - this document
     */
    private static final String XML = "<el xmlns:test=\"urn:test\" />";


    /**
     * Parse the XML using DOM
     *
     * @throws Exception
     */
    @Test
    public void testDocumentBuilderSource() throws Exception
    {
        org.w3c.dom.Element element = parseDOM(XML);

        final String namespace = element.getAttributeNode("xmlns:test").getNamespaceURI();

        assertEquals("DocumentBuilder output", JDOMConstants.NS_URI_XMLNS, namespace);
    }


    /**
     * Parse the XML using JDOM, convert to DOM
     *
     * @throws Exception
     */
    @Test
    public void testJdomToDom() throws Exception
    {
        org.w3c.dom.Element element = jdomToDom(parseJDOM(XML)); // load JDOM and convert to DOM

        final String namespace = element.getAttributeNode("xmlns:test").getNamespaceURI();

        assertEquals("SAXBuilder->DOMOutputter output", JDOMConstants.NS_URI_XMLNS, namespace);
    }


    /**
     * Parse the XML using DOM, then convert to JDOM and then finally back to DOM
     *
     * @throws Exception
     */

    @Test
    public void testDomToJdomToDom() throws Exception
    {
        org.w3c.dom.Element element = jdomToDom(domToJdom(parseDOM(XML))); // load DOM, convert to JDOM and back to DOM

        final String namespace = element.getAttributeNode("xmlns:test").getNamespaceURI();

        assertEquals("DocumentBuilder->DOMBuilder->DOMOutputter output", JDOMConstants.NS_URI_XMLNS, namespace);
    }


    private Element parseJDOM(String xml) throws Exception
    {
        final StringReader src = new StringReader(xml);

        return new SAXBuilder().build(src).getRootElement();
    }


    private org.w3c.dom.Element parseDOM(String xml) throws Exception
    {
        InputSource src = new InputSource(new StringReader(xml));

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);

        DocumentBuilder documentBuilder = factory.newDocumentBuilder();

        Document result = documentBuilder.parse(src);

        return result.getDocumentElement();
    }


    /**
     * Converts a JDOM Element to a DOM Element
     *
     * @param element
     *
     * @return
     *
     * @throws Exception
     */
    private org.w3c.dom.Element jdomToDom(org.jdom2.Element element) throws Exception
    {
        return new DOMOutputter().output(element);
    }


    /**
     * Converts a DOM Element to a JDOM Element
     *
     * @param element
     *
     * @return
     *
     * @throws Exception
     */
    private org.jdom2.Element domToJdom(org.w3c.dom.Element element) throws Exception
    {
        final DOMBuilder builder = new DOMBuilder();

        return builder.build(element);
    }
}

On our machines (Oracle Java 1.7, JDOM 2.0.4) the output is as follows:

testDocumentBuilderSource: pass
testJdomToDom: fail
    java.lang.AssertionError: SAXBuilder->DOMOutputter output expected:<http://www.w3.org/2000/xmlns/> but was:<null>
testDomToJdomToDom: fail
    java.lang.AssertionError: DocumentBuilder->DOMBuilder->DOMOutputter output expected:<http://www.w3.org/2000/xmlns/> but was:<null>
@rolfl
Collaborator
rolfl commented Apr 12, 2013

Well, that's a comprehensive issue report, and a suprising problem. Thanks. I will look in to it shortly.

@petergeneric
Contributor

I've got a patch to AbstractDOMOutputProcessor that should work at petergeneric@c27a4fc and a unit test as part of petergeneric@2c32cc5 (it doesn't use the same structure as the other jdom unit tests though - added it in a separate commit for clarity)

@rolfl
Collaborator
rolfl commented Apr 12, 2013

I pulled your fixes right in - b174b37 and 25153ea . Thanks

@rolfl rolfl closed this Apr 12, 2013
@petergeneric petergeneric added a commit to petergeneric/stdlib that referenced this issue May 5, 2015
@petergeneric petergeneric Remove Base64 (we depend on Java 1.8 so can use the native Base64 imp…
…lementation) and FixedDOMOutputProcessor (only necessary for an old JDOM2 version without the fix for hunterhacker/jdom#115 )
d705e81
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment