Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Json to XML MappedXMLStreamReader: Several Namespace Handling Problems #3

Open
sberyozkin opened this issue May 14, 2015 · 1 comment

Comments

@sberyozkin
Copy link
Contributor

In the JSON to XML case, the MappedNamespaceConvention maps the namespace defined in the key of the JSON object to the parent node. This is not correct it must be mapped to the node.

For example, if you have the JSON docment

{"p1.A":{"p2.B":"value"}}

with the xml2JsonNs map:

Map<String, String> xml2JsonNs = new TreeMap<String, String>();
xml2JsonNs.put("http://namespaceOfElementA", "p1");
xml2JsonNs.put("http://namespaceOfElementB", "p2");

then you get currently the result

value

However correct would be

value

I attached the patch.

Meanwhile I detected further problems concerning the namespace handling.

if you have the JSON document
{"simple.root":{"WithoutNs":"value"}}
then you currently get as XML
value
But correct is:
value
because the element "WithoutNS" does not have a namespace.

Secondly the attribute NS are not treated correctly:
If you have
{"simple.root":{"WithoutNs":
{"@ns.attrNs":"ans_v","$":"value"}

}}
then you get
value
But correct is :
value

So I updated the patch to correct these errors also.

Meanwhile my impression is that it would be better to use also for the elements prefixes in the XML because you need the prefixes anyway for the attributes with namespaces.

Regards Franz Forsthofer

@sberyozkin
Copy link
Contributor Author

Index: src/main/java/org/codehaus/jettison/Convention.java

--- src/main/java/org/codehaus/jettison/Convention.java (revision 159)
+++ src/main/java/org/codehaus/jettison/Convention.java (working copy)
@@ -23,7 +23,7 @@

public interface Convention {

  • void processAttributesAndNamespaces(Node n, JSONObject object)
  • void processAttributesAndNamespaces(Node n, JSONObject object, String key)
    throws JSONException, XMLStreamException;

QName createQName(String name, Node node) throws XMLStreamException;

Index: src/main/java/org/codehaus/jettison/Node.java

--- src/main/java/org/codehaus/jettison/Node.java (revision 159)
+++ src/main/java/org/codehaus/jettison/Node.java (working copy)
@@ -53,7 +53,7 @@
this.namespaces = new LinkedHashMap();
this.attributes = new LinkedHashMap();

  •    con.processAttributesAndNamespaces(this, object);
    
  •    con.processAttributesAndNamespaces(this, object, name);
    
     keys = object.keys();
    

@@ -60,10 +60,12 @@
this.name = con.createQName(name, this);
}

  • public Node(String name, Convention con) throws XMLStreamException {

  • public Node(String name, Convention con) throws JSONException, XMLStreamException {
    this.name = con.createQName(name, this);
    this.namespaces = new HashMap();
    this.attributes = new HashMap();

  •    // must call the following method to extract the namesapce from the name
    
  •    con.processAttributesAndNamespaces(this, object, name);
    

    }

    public Node(JSONObject object) {

    Index: src/main/java/org/codehaus/jettison/badgerfish/BadgerFishConvention.java

    --- src/main/java/org/codehaus/jettison/badgerfish/BadgerFishConvention.java (revision 159)
    +++ src/main/java/org/codehaus/jettison/badgerfish/BadgerFishConvention.java (working copy)
    @@ -32,9 +32,12 @@
    super();
    }

  • public void processAttributesAndNamespaces(Node n, JSONObject object)

  • public void processAttributesAndNamespaces(Node n, JSONObject object, String key)
    throws JSONException, XMLStreamException {
    // Read in the attributes, and stop when there are no more

  •    if (object == null){
    
  •        return;
    
  •    }
     for (Iterator itr = object.keys(); itr.hasNext();) {
         String k = (String) itr.next();
    

Index: src/main/java/org/codehaus/jettison/mapped/MappedNamespaceConvention.java

--- src/main/java/org/codehaus/jettison/mapped/MappedNamespaceConvention.java (revision 159)
+++ src/main/java/org/codehaus/jettison/mapped/MappedNamespaceConvention.java (working copy)
@@ -92,12 +92,29 @@
/* (non-Javadoc)
* @see org.codehaus.xson.mapped.Convention#processNamespaces(org.codehaus.xson.Node, org.json.JSONObject)
*/

  • public void processAttributesAndNamespaces( Node n, JSONObject object ) throws JSONException {

  • public void processAttributesAndNamespaces( Node n, JSONObject object, String key) throws JSONException {

  •    // namespace is defined on the key
    
  •    int dot = key.lastIndexOf( '.' );
    
  •    if ( dot != -1 ) {
    
  •        String jns = key.substring( 0, dot );
    
  •        String xns = getNamespaceURI( jns );
    
  •        if (!ignoreNamespaces){
    
  •            n.setNamespace( "", xns );
    
  •        }
    
  •    }else{
    
  •        // you must set the empty namespace, because we do not use prefixes
    
  •        if (!ignoreNamespaces){
    
  •            n.setNamespace("", "");
    
  •        }
    
  •    }
    
  •    if (object == null){
    
  •        return;
    
  •    }
     // Read in the attributes, and stop when there are no more
     for (Iterator<?> itr = object.keys(); itr.hasNext();) {
         String k = (String) itr.next();
    
  •      if ( this.supressAtAttributes ) {
             if ( k.startsWith( attributeKey ) ) {
                 k = k.substring( 1 );
    

    @@ -109,7 +126,7 @@
    this.jsonAttributesAsElements.add( k );
    }
    }

  •      if ( k.startsWith( attributeKey ) ) {
             Object o = object.opt( k );
             //                String value = object.optString( k );
    

    @@ -122,11 +139,11 @@
    // set namespace if one is specified on this attribute.
    String prefix = (String) pitr.next();
    String uri = jo.getString( prefix );

  •                      //                            if ( prefix.equals( "$" ) ) {
                         //                                prefix = "";
                         //                            }
    
  •                      n.setNamespace( prefix, uri );
                     }
                 }
    

    @@ -137,7 +154,10 @@
    // note that a non-prefixed attribute name implies NO namespace,
    // i.e. as opposed to the in-scope default namespace
    if ( k.contains( "." ) ) {

  •                    name = createQName( k, n );
    
  •                    name = createQName( k, true );
    
  •                    if (!ignoreNamespaces){
    
  •                        n.setNamespace(name.getPrefix(), name.getNamespaceURI());
    
  •                    }
                 }
                 else {
                     name = new QName( XMLConstants.DEFAULT_NS_PREFIX, k );
    

    @@ -146,18 +166,10 @@
    }
    itr.remove();
    }

  •        else {
    
  •            // set namespace if one is specified on this attribute.
    
  •            int dot = k.lastIndexOf( '.' );
    
  •            if ( dot != -1 ) {
    
  •                String jns = k.substring( 0, dot );
    
  •                String xns = getNamespaceURI( jns );
    
  •                n.setNamespace( "", xns );
    
  •            }
    
  •        }
     }
    

    }

  • /*

  • (non-Javadoc)

  • @see javax.xml.namespace.NamespaceContext#getNamespaceURI(java.lang.String)
    @@ -198,8 +210,10 @@

    public QName createQName( String rootName, Node node ) {

  •    return createQName( rootName );
    
  •    return createQName( rootName, false );
    

    }

  • @SuppressWarnings("unused")
    private void readAttribute( Node n, String k, JSONArray array ) throws JSONException {
    @@ -211,21 +225,24 @@

    private void readAttribute( Node n, String name, String value ) throws JSONException {

  •    QName qname = createQName( name );
    
  •    QName qname = createQName( name, false );
     n.getAttributes().put( qname, value );
    

    }

  • private QName createQName( String name ) {

  • private QName createQName( String name, boolean isAttr ) {

 int dot = name.lastIndexOf( '.' );
 QName qname = null;
 String local = name;
  •    String prefix;
    
     if ( dot == -1 ) {
         dot = 0;
    
  •        prefix = null;
     }
     else {
         local = local.substring( dot + 1 );
    
  •        prefix = name.substring( 0, dot );
     }
    
     String jns = name.substring( 0, dot );
    

    @@ -235,11 +252,17 @@
    qname = new QName( name );
    }
    else {

  •        qname = new QName( xns, local );
    
  •        if (isAttr){
    
  •            // attributes with namespace must have a prefix
    
  •            qname = new QName( xns, local, prefix );
    
  •        }else{
    
  •            qname = new QName( xns, local );
    
  •        }
     }
    
     return qname;
    

    }

  • public String createAttributeKey( String p, String ns, String local ) {

Index: src/main/java/org/codehaus/jettison/mapped/MappedXMLStreamReader.java

--- src/main/java/org/codehaus/jettison/mapped/MappedXMLStreamReader.java (revision 159)
+++ src/main/java/org/codehaus/jettison/mapped/MappedXMLStreamReader.java (working copy)
@@ -53,7 +53,7 @@
this.node = new Node(null, rootName, obj, convention);
} else {
node = new Node(rootName, convention);

  •        convention.processAttributesAndNamespaces(node, obj);
    
  •        convention.processAttributesAndNamespaces(node, obj, rootName);
         currentValue = JSONObject.NULL.equals(top) ? null : top.toString();
     }
     nodes.push(node);
    

    Index: src/test/java/org/codehaus/jettison/mapped/MappedRoundTripTest.java

    --- src/test/java/org/codehaus/jettison/mapped/MappedRoundTripTest.java (revision 0)
    +++ src/test/java/org/codehaus/jettison/mapped/MappedRoundTripTest.java (working copy)
    @@ -0,0 +1,128 @@
    +package org.codehaus.jettison.mapped;
    +
    +import java.io.ByteArrayInputStream;
    +import java.io.ByteArrayOutputStream;
    +import java.io.UnsupportedEncodingException;
    +import java.util.Map;
    +import java.util.TreeMap;
    +
    +import javax.xml.parsers.FactoryConfigurationError;
    +import javax.xml.stream.XMLEventReader;
    +import javax.xml.stream.XMLEventWriter;
    +import javax.xml.stream.XMLInputFactory;
    +import javax.xml.stream.XMLOutputFactory;
    +import javax.xml.stream.XMLStreamException;
    +import javax.xml.stream.XMLStreamReader;
    +
    +import org.codehaus.jettison.AbstractXMLInputFactory;
    +import org.codehaus.jettison.AbstractXMLOutputFactory;
    +
    +import junit.framework.TestCase;
    +
    +/** Tests XML to JSoN and JSON to XML in a round trip. */
    +public class MappedRoundTripTest extends TestCase {
    +
  • public void testRootElementWithNamespaceChildWithout() throws Exception {
  •    String input = "<?xml version='1.0' encoding='UTF-8'?><root xmlns=\"http://simple\">" + //
    
  •            "<WithoutNs xmlns=\"\">value</WithoutNs>" + //
    
  •            "</root>";
    
  •    Map<String, String> xml2JsonNs = new TreeMap<String, String>();
    
  •    xml2JsonNs.put("http://simple", "simple");
    
  •    doRoundTrip(input, input, xml2JsonNs, false);
    
  • }
  • public void testRootElementWithNamespaceChildWithoutButWithPrefixe() throws Exception {
  •    String input = "<?xml version='1.0' encoding='UTF-8'?><p:root xmlns:p=\"http://simple\">" + //
    
  •            "<WithoutNs>value</WithoutNs>" + //
    
  •            "/p:root";
    
  •    String expectedResult = "<?xml version='1.0' encoding='UTF-8'?><root xmlns=\"http://simple\">" + //
    
  •            "<WithoutNs xmlns=\"\">value</WithoutNs>" + //
    
  •            "</root>";
    
  •    Map<String, String> xml2JsonNs = new TreeMap<String, String>();
    
  •    xml2JsonNs.put("http://simple", "simple");
    
  •    doRoundTrip(input, expectedResult, xml2JsonNs, false);
    
  • }
  • public void testAttributesWithAndWithoutNs() throws Exception {
  •    String input = "<?xml version='1.0' encoding='UTF-8'?><root xmlns=\"http://simple\">" + //
    
  •            "<WithoutNs xmlns=\"\" xmlns:ns=\"http://attrNs\" attr=\"a_value\" ns:attrNs=\"ans_v\">value</WithoutNs>" + //
    
  •            "</root>";
    
  •    Map<String, String> xml2JsonNs = new TreeMap<String, String>();
    
  •    xml2JsonNs.put("http://simple", "simple");
    
  •    xml2JsonNs.put("http://attrNs", "ns");
    
  •    doRoundTrip(input, input, xml2JsonNs, false);
    
  • }
  • public void testIgnoreNs() throws Exception {
  •    String input = "<?xml version='1.0' encoding='UTF-8'?><root xmlns=\"http://simple\">" + //
    
  •            "<WithoutNs xmlns=\"\" xmlns:ns=\"http://attrNs\" attr=\"a_value\" ns:attrNs=\"ans_v\">value</WithoutNs>" + //
    
  •            "</root>";
    
  •    Map<String, String> xml2JsonNs = new TreeMap<String, String>();
    
  •    xml2JsonNs.put("http://simple", "simple");
    
  •    xml2JsonNs.put("http://attrNs", "ns");
    
  •    String expected = "<?xml version='1.0' encoding='UTF-8'?><root>" + //
    
  •            "<WithoutNs attr=\"a_value\" attrNs=\"ans_v\">value</WithoutNs>" + //
    
  •            "</root>";
    
  •    doRoundTrip(input, expected, xml2JsonNs, true);
    
  • }
  • private void doRoundTrip(String input, String expectedResult, Map<String, String> xml2JsonNs, boolean ignoreNamespaces)
  •        throws FactoryConfigurationError, XMLStreamException, UnsupportedEncodingException, Exception {
    
  •    //System.out.println(input);
    
  •    byte[] json = xmlToJson(input.getBytes("UTF-8"), xml2JsonNs, ignoreNamespaces);
    
  •    String result = jsonToXml(json, xml2JsonNs, ignoreNamespaces);
    
  •    //System.out.println(result);
    
  •    assertEquals(expectedResult, result);
    
  • }
  • private byte[] xmlToJson(byte[] xml, Map<String, String> xml2JsonNs, boolean ignoreNamespaces) throws FactoryConfigurationError,
  •        XMLStreamException, UnsupportedEncodingException {
    
  •    XMLInputFactory readerFactory = XMLInputFactory.newInstance();
    
  •    XMLEventReader eventReader = readerFactory.createXMLEventReader(new ByteArrayInputStream(xml));
    
  •    Configuration configuration = new Configuration();
    
  •    configuration.setXmlToJsonNamespaces(xml2JsonNs);
    
  •    configuration.setIgnoreNamespaces(ignoreNamespaces);
    
  •    AbstractXMLOutputFactory writerFactory = new MappedXMLOutputFactory(configuration);
    
  •    ByteArrayOutputStream os = new ByteArrayOutputStream();
    
  •    XMLEventWriter eventWriter = writerFactory.createXMLEventWriter(os, "UTF-8");
    
  •    eventWriter.add(eventReader);
    
  •    eventWriter.close();
    
  •    byte[] bytes = os.toByteArray();
    
  •    //System.out.println(new String(bytes, "UTF-8"));
    
  •    return bytes;
    
  • }
  • private String jsonToXml(byte[] json, Map<String, String> xml2JsonNs, boolean ignoreNamespaces) throws Exception {
  •    Configuration configuration = new Configuration();
    
  •    configuration.setXmlToJsonNamespaces(xml2JsonNs);
    
  •    configuration.setIgnoreNamespaces(ignoreNamespaces);
    
  •    AbstractXMLInputFactory inputFactory = new MappedXMLInputFactory(configuration);
    
  •    XMLStreamReader streamReader = inputFactory.createXMLStreamReader(new ByteArrayInputStream(json), "UTF-8");
    
  •    XMLInputFactory readerFactory = XMLInputFactory.newInstance();
    
  •    XMLEventReader eventReader = readerFactory.createXMLEventReader(streamReader);
    
  •    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    
  •    XMLOutputFactory writerFactory = XMLOutputFactory.newInstance();
    
  •    XMLEventWriter eventWriter = writerFactory.createXMLEventWriter(baos, "UTF-8");
    
  •    eventWriter.add(eventReader);
    
  •    eventWriter.close();
    
  •    return new String(baos.toByteArray(), "UTF-8");
    
  • }

+}

Property changes on: src/test/java/org/codehaus/jettison/mapped/MappedRoundTripTest.java


Added: svn:mime-type

-0,0 +1

+text/plain
\ No newline at end of property

Index: src/test/java/org/codehaus/jettison/mapped/MappedXMLStreamReaderTest.java

--- src/test/java/org/codehaus/jettison/mapped/MappedXMLStreamReaderTest.java (revision 159)
+++ src/test/java/org/codehaus/jettison/mapped/MappedXMLStreamReaderTest.java (working copy)
@@ -15,25 +15,38 @@
*/
package org.codehaus.jettison.mapped;

+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;

import javax.xml.namespace.QName;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.stream.Location;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.events.Namespace;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;

import junit.framework.TestCase;

+import org.codehaus.jettison.AbstractXMLInputFactory;
+import org.codehaus.jettison.AbstractXMLOutputFactory;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.codehaus.jettison.json.JSONTokener;
@@ -1022,4 +1035,58 @@
assertEquals(refEvents, mpdEvents);
}

  • public void testNamspace() throws Exception {
  •    JSONObject obj = new JSONObject("{\"p1.A\":{\"p2.B\":\"value\"}}");
    
  •    Map<String, String> xml2JsonNs = new TreeMap<String, String>();
    
  •    xml2JsonNs.put("http://namespaceOfElementA", "p1");
    
  •    xml2JsonNs.put("http://namespaceOfElementB", "p2");
    
  •    Configuration configuration = new Configuration();
    
  •    configuration.setXmlToJsonNamespaces(xml2JsonNs);
    
  •    MappedNamespaceConvention con = new MappedNamespaceConvention(configuration);
    
  •    MappedXMLStreamReader reader = new MappedXMLStreamReader(obj, con);
    
  •    XMLInputFactory readerFactory = XMLInputFactory.newInstance();
    
  •    XMLEventReader eventReader = readerFactory.createXMLEventReader(reader);
    
  •    assertTrue(eventReader.hasNext());
    
  •    // document event
    
  •    eventReader.next();
    
  •    assertTrue(eventReader.hasNext());
    
  •    XMLEvent event = eventReader.nextEvent();
    
  •    assertTrue(event.isStartElement());
    
  •    StartElement el = event.asStartElement();
    
  •    assertEquals("A", el.getName().getLocalPart());
    
  •    Iterator<Namespace> ns = el.getNamespaces();
    
  •    assertTrue(ns.hasNext());
    
  •    assertEquals("http://namespaceOfElementA", ns.next().getNamespaceURI());
    
  •    assertTrue(eventReader.hasNext());
    
  •    event = eventReader.nextEvent();
    
  •    assertTrue(event.isStartElement());
    
  •    el = event.asStartElement();
    
  •    assertEquals("B", el.getName().getLocalPart());
    
  •    ns = el.getNamespaces();
    
  •    assertTrue(ns.hasNext());
    
  •    assertEquals("http://namespaceOfElementB", ns.next().getNamespaceURI());
    
  •    assertTrue(eventReader.hasNext());
    
  •    event = eventReader.nextEvent();
    
  •    assertTrue(event.isCharacters());
    
  •    assertEquals("value", event.asCharacters().getData());
    
  •    assertTrue(eventReader.hasNext());
    
  •    event = eventReader.nextEvent();
    
  •    assertTrue(event.isEndElement());
    
  •    assertTrue(eventReader.hasNext());
    
  •    event = eventReader.nextEvent();
    
  •    assertTrue(event.isEndElement());
    
  •    assertTrue(eventReader.hasNext());
    
  •    event = eventReader.nextEvent();
    
  •    assertTrue(event.isEndDocument());
    
  •    assertFalse(eventReader.hasNext());
    
  • }
  • }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant