How to provide JACKSON with the namespace-mapper in the code? - json

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?

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
http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html
http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html

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").

Related

JAXB Namespace issue

This question is for Mr.Blaise Doughan on JAXB Namespace
I have situation where,
Have a sample.xsd (old version - no namespace ). Generated JAXB classes using the XJC for the same XSD file. I got one example that uses the JAXB classes to unmarshal the XML data file , based on the XSD.
The sample.xsd file got changed (new version - added namespace). Again generated JAXB classes using the XJC for the new XSD file. The Example is updated so that it can now work for new XSD file
Now I got a situation , where iam getting XML data file based on old XSD and I would like to use the updated example file to unmarshal the old XML data.
One solution I could see , generating two object factory one with namespace and one without namespace. Can we do that? if so I can use the appropriate Object factory based on the my XML data I get.
Or would like to know , how can I generate JAXB classes for both XSD files , but XJC is not generating , it shows error - No changes detected in schema or binding files - skipping source generation.
Can I create a wrapper over the new Object Factory so that it can handle both ?
Please do provide me with some solution so that I can unmarshal the old file with new JAXB classes. Can
Apply a Namespace
In the case where the input XML does not have a namespace you can leverage a SAX XMLFilter to apply a namespace.
import org.xml.sax.*;
import org.xml.sax.helpers.XMLFilterImpl;
public class NamespaceFilter extends XMLFilterImpl {
private static final String NAMESPACE = "http://www.example.com/customer";
#Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
super.endElement(NAMESPACE, localName, qName);
}
#Override
public void startElement(String uri, String localName, String qName,
Attributes atts) throws SAXException {
super.startElement(NAMESPACE, localName, qName, atts);
}
}
Do the Unmarshal
The unmarshalling is done leveraging JAXB's UnmarshallerHandler as the ContentHandler
import javax.xml.bind.*;
import javax.xml.parsers.*;
import org.xml.sax.*;
public class Demo {
public static void main(String[] args) throws Exception {
// Create the JAXBContext
JAXBContext jc = JAXBContext.newInstance(Customer.class);
// Create the XMLFilter
XMLFilter filter = new NamespaceFilter();
// Set the parent XMLReader on the XMLFilter
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
filter.setParent(xr);
// Set UnmarshallerHandler as ContentHandler on XMLFilter
Unmarshaller unmarshaller = jc.createUnmarshaller();
UnmarshallerHandler unmarshallerHandler = unmarshaller
.getUnmarshallerHandler();
filter.setContentHandler(unmarshallerHandler);
// Parse the XML
InputSource xml = new InputSource("src/blog/namespace/sax/input.xml");
filter.parse(xml);
Customer customer = (Customer) unmarshallerHandler.getResult();
// Marshal the Customer object back to XML
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);
}
}
For More Information
http://blog.bdoughan.com/2012/11/applying-namespace-during-jaxb-unmarshal.html

How send bson via Spring #response body

I am using #Responsebody String to send JSON.
part of my code is below.(I am sorry for open it all)
#RequestMapping(value = "/getSomeList", method = RequestMethod.GET ,
headers="Accept=application/json", produces = "text/plain;charset=UTF-8")
public #ResponseBody String getThumbList(
#RequestParam("con_id") String con_id) throws Exception{
return json;
}
And actually it sends Json. But my client requests Bson type. How can I change Json to Bson without editing global format(my json is actually just string and I heard that spring can not response bson. Is that right?).
You need to register a HttpMessageConverter that can convert to BSON.
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="MyBsonHttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
Unfortunaly Spring has no Bson Http Message Converter, so you have to implement your own. (Or have more luck then me while google for one).
This is an old question, but it is nigh the only thing I can find on the web on this topic.
So here's how to do it. Assuming you're using Jackson as your JSON library in Spring.
Add a dependency to Michel Krämer's bson4jackson library
Create a class like so:
import com.fasterxml.jackson.databind.ObjectMapper;
import de.undercouch.bson4jackson.BsonFactory;
import de.undercouch.bson4jackson.BsonGenerator;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import javax.annotation.Nonnull;
public class MappingJackson2BsonHttpMessageConverter
extends AbstractJackson2HttpMessageConverter
{
public MappingJackson2BsonHttpMessageConverter(#Nonnull Jackson2ObjectMapperBuilder builder) {
super(bsonObjectMapper(builder), MediaType.parseMediaType("application/bson"));
}
#Nonnull
private static ObjectMapper bsonObjectMapper(#Nonnull Jackson2ObjectMapperBuilder om){
BsonFactory f = new BsonFactory();
f.configure(BsonGenerator.Feature.ENABLE_STREAMING, true);
return om.factory(f).build();
}
}
Add it as a #Bean to your configuration, or annotate it with #Component and make sure it's in your #ComponentScan's path.
That's it. Now, if you declare your MVC endpoint with #RequestMapping(produces = "application/bson") (in some form or another), the output will be the BSON encoding of your ResponseEntity's body.
This works with Jackson-annotated Objects which you'd normally serialize to JSON; and all the configuration you did to the Jackson ObjectMapper through Spring, modules or annotations will apply.
It also works for input as well as for output.
It also works with Feign clients.
I assume it also works with Spring's RestTemplate.

Is #XmlElement necessary for Jaxb

My question is, whether it is necessary to add #XmlElement before each element in your pojo to be picked up by jaxb, when making a JSON response. I am using jersey-json 1.17 . The reason I ask this is because, the example given on Jersey site does not use the annotation.
I get an out put as {}, but when I add #XmlElement before the attributes, I get the expected JSON output. Am I doing something wrong, because of which my JSON string is empty ?
My code :
The vertices list is populated in the constructor.
This produces the wrong output of {}
#XmlRootElement
public class SquareModel {
List<Float> vertices = new ArrayList<Float>();
....
}
Whereas this produces the a correct JSON string :
#XmlRootElement
public class SquareModel {
#XmlElement(name="vertices")
List<Float> vertices = new ArrayList<Float>();
....
}
My resource class which returns the JSON
#GET
#Produces(MediaType.APPLICATION_JSON)
public SquareModel getJsonString() {
return new SquareModel();
}
Thanks :)
No, by default a JAXB (JSR-22#) implementation will treat all public fields and properties (get/set combinations) as mapped (not requiring the #XmlElement annotation).
http://blog.bdoughan.com/2012/07/jaxb-no-annotations-required.html
If you wish to annotate a field I would recommend annotating your class with #XmlAccessorType(XmlAccessType.FIELD)
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
According to this http://jersey.java.net/nonav/documentation/latest/json.html#json.jaxb.approach.section
You should have this annotation (I'm also using it in my code, even though it XML oriented, but it gives me cool JSON also)
Taking this approach will save you a lot of time, if you want to
easily produce/consume both JSON and XML data format. Because even
then you will still be able to use a unified Java model. Another
advantage is simplicity of working with such a model, as JAXB
leverages annotated POJOs and these could be handled as simple Java
beans.
A disadvantage of JAXB based approach could be if you need to work
with a very specific JSON format. Then it could be difficult to find a
proper way to get such a format produced and consumed. This is a
reason why a lot of configuration options are provided, so that you
can control how things get serialized out and deserialized back.
Following is a very simple example of how a JAXB bean could look like.
Example 5.3. Simple JAXB bean implementation
#XmlRootElement
public class MyJaxbBean {
public String name;
public int age;
public MyJaxbBean() {} // JAXB needs this
public MyJaxbBean(String name, int age) {
this.name = name;
this.age = age;
}
}

JSON Provider builds wrong String field, when value is 'true' or 'false'

I have a problem. Could you please help me.
I have an application: CXF+Spring+JAXB+REST. And I try to generate Response using JSONProvider class
There is a bean class:
#XmlRootElement
class Foo {
#XmlElement
private String bar;
}
When I set to the field value:
setBar("true")
or
setBar("false");
JSONProvider returns to me:
"bar":false
But, I expect
"bar":"false"
Because I use String type. What should I do with that?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
The JAXB (JSR-222) specification does not cover JSON-binding. In a REST/JAX-RS context it is up to the provider to apply the JAXB mapping rules to a JSON representation. There are 3 different approaches that are being used today:
Use a JAXB implementation with a library like Jettison. Jettison implements the StAX interfaces but interprets JSON. The advantage of this approach is that it can be used with any JAXB implementation to produce/consume JSON, the disadvantage of this approach is that Jettison does not have enough information to do a perfect JSON representation. Jettison receives the String "true" without knowing what the corresponding Java type was, so it decides to represent it as a JSON boolean, since it is probably the desired output most of the time.
REST/JAX-RS implementation leverages a specific JAXB implementation to produce JSON. This approach is similar to the first, except that the REST/JAX-RS provider makes proprietary calls to the JAXB provider to make more educated guesses to what the JSON representation should be.
The JAXB implementation provides the JSON-binding implementation. Currently EclipseLink MOXy is the only JAXB implementation to support JSON-binding.
MOXy EXAMPLE
Root
Below is a domain object with String and boolean fields. I've also added fields annotated with #XmlSchemaType which can be used to override the default representation.
package forum11145933;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
String barString;
boolean barBoolean;
#XmlSchemaType(name="boolean")
String barStringWithXmlTypeBoolean;
#XmlSchemaType(name="string")
boolean barBooleanWithXmlTypeString;
}
jaxb.properties
To specify MOXy as your JAXB provider you need to add a file called jaxb.properties in the same package as your domain model with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
The following code demonstrates how to marshal the domain object to JSON. Note that there are no compile time dependencies on MOXy.
package forum11145933;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Root root = new Root();
root.barString = "true";
root.barBoolean = true;
root.barStringWithXmlTypeBoolean = "true";
root.barBooleanWithXmlTypeString = true;
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("eclipselink.media-type", "application/json");
marshaller.marshal(root, System.out);
}
}
Output
The following is the output from running the Demo code. Note how the boolean and String properties are written out correctly. Also notice how the use of the #XmlSchemaType annotation allows us to marshal a boolean as a String and vice versa.
{
"root" : {
"barString" : "true",
"barBoolean" : true,
"barStringWithXmlTypeBoolean" : true,
"barBooleanWithXmlTypeString" : "true"
}
}
MOXy & JAX-RS
MOXy includes MOXyJsonProvider which can be used to enable MOXy as the JSON provider in your JAX-RS environment:
http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html

One domain model, multiple json views

We have a set of domain classes which are serialized to json via jackson using jersey services. We are currently annotating the classes with JAXB (although we're not tied to that). This works fine. But we want to offer different serializations of the classes for different use cases.
Web site
Mobile apps
Admin tool
Public API
In each of these cases there are different fields which we may or may not want included in the json view. For example, the admin tool might need some parameters for setting permissions on data. The mobile client needs a different URL to a media stream than the website. The website has particular naming conventions it needs for fields.
What is the best practice for managing different mappings of json for different service endpoints in Jersey?
Thanks!
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
MOXy offers JSON-binding based on JAXB annotations as well as an external binding document that allows you to apply alternate mappings to a domain model. I will demonstrate below with an example.
Metadata as JAXB Annotations
Below is a simple Java model mapping with the standard JAXB annotations.
package forum10761762;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
int id;
#XmlElement(name="first-name")
String firstName;
#XmlElement(name="last-name")
String lastName;
}
Alternate Metadata #1 (alternate1.xml)
Here we will use the XML mapping document to unmap a couple of fields by making them #XmlTransient.
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum10761762">
<java-types>
<java-type name="Customer">
<java-attributes>
<xml-transient java-attribute="id"/>
<xml-transient java-attribute="firstName"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
Alternate Metadata #2 (alternate2.xml)
Here we will map the Java model to a different JSON structure using MOXy's path based mapping extension.
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum10761762">
<java-types>
<java-type name="Customer">
<java-attributes>
<xml-element java-attribute="firstName" xml-path="personalInfo/firstName/text()"/>
<xml-element java-attribute="lastName" xml-path="personalInfo/lastName/text()"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
Demo Code
package forum10761762;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
public class Demo {
public static void main(String[] args) throws Exception {
Customer customer = new Customer();
customer.id = 123;
customer.firstName = "Jane";
customer.lastName = "Doe";
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
// Output #1
JAXBContext jc1 = JAXBContext.newInstance(new Class[] {Customer.class}, properties);
marshal(jc1, customer);
// Output #2
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "forum10761762/alternate1.xml");
JAXBContext jc2 = JAXBContext.newInstance(new Class[] {Customer.class}, properties);
marshal (jc2, customer);
// Output #2
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "forum10761762/alternate2.xml");
JAXBContext jc3 = JAXBContext.newInstance(new Class[] {Customer.class}, properties);
marshal(jc3, customer);
}
private static void marshal(JAXBContext jc, Object object) throws Exception {
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(object, System.out);
System.out.println();
}
}
Output
Below is the output from running the demo code. Note from the same object model 3 different JSON documents were produced.
{
"id" : 123,
"first-name" : "Jane",
"last-name" : "Doe"
}
{
"last-name" : "Doe"
}
{
"id" : 123,
"personalInfo" : {
"firstName" : "Jane",
"lastName" : "Doe"
}
}
For More Information (from my blog)
JSON Binding with EclipseLink MOXy - Twitter Example
MOXy as Your JAX-RS JSON Provider - MOXyJsonProvider
MOXy's XML Metadata in a JAX-RS Service
Specifying EclipseLink MOXy as Your JAXB Provider