1

I am trying to serialize a JAXB generated class. With Jettison, I am able to create a hash-map mapping the XML namespaces to any JSON prefix. With Jettison, I also get the case sensitivity right in the serialization. With JACKSON, it is all lower case. So, it seems that Jettison is able to understand XMLRootElement(name=…) much better.

How do I make JACKSON better understand JAXB annotations like XMLRootElement?

How do I provide JACKSON with a XML→JSON namespace mapper?

stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268
Behzad Pirvali
  • 764
  • 3
  • 10
  • 28

2 Answers2

3

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.

EclipseLink MOXy is a JAXB (JSR-222) compliant implementation. In EclipseLink 2.4.0 we introduced JSON-binding. Since MOXy is a JAXB implementation you will find the JSON output MOXy produces will be very consistent with XML output based on the same metadata. I will demonstrate below with an example.


DOMAIN MODEL

Below is the domain model I will use for this answer. For more information on specifying namespace information in a JAXB model see: http://blog.bdoughan.com/2010/08/jaxb-namespaces.html

package-info

@XmlSchema(
        namespace="http://www.example.com/A",
        elementFormDefault=XmlNsForm.QUALIFIED,
        xmlns={
                @XmlNs(prefix="a",namespaceURI = "http://www.example.com/A"),
                @XmlNs(prefix="b",namespaceURI = "http://www.example.com/B")
        }
)
package forum13214306;

import javax.xml.bind.annotation.*;

Customer

package forum13214306;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

    String firstName;

    @XmlElement(namespace="http://www.example.com/B")
    String lastName;

}

XML HANDLING

Below is an example of how the domain model corresponds to an XML representation.

Demo

package forum13214306;

import java.io.File;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Customer.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum13214306/input.xml");
        Customer customer = (Customer) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(customer, System.out);
    }

}

input.xml/Output

<?xml version="1.0" encoding="UTF-8"?>
<a:customer xmlns:b="http://www.example.com/B" xmlns:a="http://www.example.com/A">
   <a:firstName>Jane</a:firstName>
   <b:lastName>Doe</b:lastName>
</a:customer>

JSON HANDLING - WITHOUT NAMESPACES

Namespaces aren't a JSON concept so I would recommend not simulating them if you can avoid this. Below I'll demonstrate that MOXy doesn't need them. Note the exact same domain model and JAXBContext is used here that was used for the XML document with namespaces.

jaxb.properties

To specify MOXy as your JSON provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/search/label/jaxb.properties).

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

Demo

To enable JSON binding the MEDIA_TYPE property needs to be enable on the Marshaller and Unmarshaller.

package forum13214306;

import java.io.File;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.MarshallerProperties;
import org.eclipse.persistence.jaxb.UnmarshallerProperties;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Customer.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json");
        File json = new File("src/forum13214306/input.json");
        Customer customer = (Customer) unmarshaller.unmarshal(json);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
        marshaller.marshal(customer, System.out);
    }

}

input.json/Output

Below is the input to and output from running the demo code. Note how there is no simulated namespace information in the JSON document.

{
   "customer" : {
      "firstName" : "Jane",
      "lastName" : "Doe"
   }
}

JSON HANDLING - WITH SIMULATED NAMESPACES

Demo

If you really want to simulate namespaces in your JSON document you can leverage the NAMESPACE_PREFIX_MAPPER property on the Marshaller and Unmarshaller to do so.

package forum13214306;

import java.io.File;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.MarshallerProperties;
import org.eclipse.persistence.jaxb.UnmarshallerProperties;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Customer.class);

        Map<String, String> namespaceToPrefixMap = new HashMap<String, String>(2);
        namespaceToPrefixMap.put("http://www.example.com/A", "a");
        namespaceToPrefixMap.put("http://www.example.com/B", "b");

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json");
        unmarshaller.setProperty(UnmarshallerProperties.JSON_NAMESPACE_PREFIX_MAPPER, namespaceToPrefixMap);
        File json = new File("src/forum13214306/input.json");
        Customer customer = (Customer) unmarshaller.unmarshal(json);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
        marshaller.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER, namespaceToPrefixMap);
        marshaller.marshal(customer, System.out);
    }

}

input.json/Output

{
   "a.customer" : {
      "a.firstName" : "Jane",
      "b.lastName" : "Doe"
   }
}

FOR MORE INFORMATION

bdoughan
  • 147,609
  • 23
  • 300
  • 400
1

JSON does not have namespaces, unlike XML. So why do you feel you need namespace mapping? Databinding means mapping between Java POJOs and data format, using features of the format. For XML this includes namespaces, choice of elements vs attributes and so on. With JSON much of this complexity is removed, meaning that property names are used as is.

As to JAXB annotations: Jackson has its own set of annotations that are better match, but if you do want to use JAXB annotations as additional or alternative configuration source, you will need to use JAXB Annotation module. But as to using XMLRootElement, it is unnecessary for JSON: JSON Objects do not have name.

I do not know what you mean by "get the case sensitivity right in the serialization" -- right in what sense? What is the problem? You will need to give an example of POJO definition as well as expected JSON.

Finally, keep in mind that there is no need for JSON and XML notations to look like each other. These are different data formats with different logical data models, and naturally differing mappings: JSON has native distinction between Arrays and Objects, for example; whereas XML has to use Elements for both. And XML has namespaces, attributes, that JSON lacks. These lead to different natural mappings, and attempts to "unify" the two lead to somewhat unnatural results for one or the other, or both. In case of Jettison (and frameworks that use it), it's ugly JSON ("franken-JSON").

StaxMan
  • 113,358
  • 34
  • 211
  • 239
  • Thank U so much for your very informative comments except for: 1) How do one avoids naming conflict without namespaces? 2) You get JAXB annotations automatically as part of running JAXB on your *.xsd file. Ideally, those annotations should drive the JSON creation as well, don't you agree? 3) So, why is JaxbAnnotationIntrospector deprecated? Thanks again – Behzad Pirvali Nov 22 '12 at 16:02
  • (1) By avoiding conflicting names. Where would you be getting conflicting names to begin with? (that is, what is the actual problem) (2) XSD is for XML, not for JSON, so it is not optimal definition to begin with in general. But since that's what you have, and you are using JAXB tools, yes (one could use other XSD tools with other productions). (3) JaxbAnnotationIntrospector is definitely not deprecated, but you may be using constructor that is? Hope this helps! – StaxMan Nov 22 '12 at 19:40