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

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

Related

JAX-RS / Jersey ".get(Integer.class)" and single JSON primitive (Integer) values?

I have a JAX-RS WebService with the following method:
#Path("/myrest")
public class MyRestResource {
...
#GET
#Path("/getInteger")
#Produces(APPLICATION_JSON)
public Integer getInteger() {
return 42;
}
When accessed using this snipped:
#Test
public void testGetPrimitiveWrapers() throws IOException {
// this works:
assertEquals(new Integer(42), new ObjectMapper().readValue("42", Integer.class));
// that fails:
assertEquals(new Integer(42), resource().path("/myrest/getInteger").get(Integer.class));
}
I get the following exception:
com.sun.jersey.api.client.ClientResponse getEntity
SEVERE: A message body reader for Java class java.lang.Integer, and Java type class java.lang.Integer, and MIME media type application/json was not found
com.sun.jersey.api.client.ClientResponse getEntity
SEVERE: The registered message body readers compatible with the MIME media type are: application/json
...
The problem is just with returning single primitive values (int/boolean) or their wrapper classes. Returning other POJO classes is not the problemen so I guess all the answers regarding JSONConfiguration.FEATURE_POJO_MAPPING and JAXB annotations do not apply here.
Or which annotation should I use to describe the return type if I don't have access to its
class source?
Using ngrep I can verify that just the String "42" is returned by the webservice. Thats a valid JSON "value" but not a valid JSON "text" according to the spec. So is my problem on the client or the server side?
I tried activating JSONConfiguration natural/badgerfish according to http://tugdualgrall.blogspot.de/2011/09/jax-rs-jersey-and-single-element-arrays.html but with no success (ngrep still shows just "42"). Would that be the right path?
Any ideas are appreciated!
This is a recognized bug in Jackson, which has been touted (incorrectly in my opinion) as a feature. Why do I consider it a bug? Because while serialization works, deserialization definitely does not.
In any case, valid JSON cannot be generated from your current return type, so I would recommend creating a wrapper class:
class Result<T> {
private T data;
// constructors, getters, setters
}
#GET
#Path("/getInteger")
#Produces(APPLICATION_JSON)
public Result<Integer> getInteger() {
return new Result<Integer)(42);
}
Alternatively, you can elect to wrap root values, which will automatically encapsulate your data in a top level JSON object, keyed by the objects simple type name - but note that if this option is used that all generated JSON will be wrapped (not just for primitives):
final ObjectMapper mapper = new ObjectMapper()
.configure(SerializationFeature.WRAP_ROOT_VALUE, true)
.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
final String serializedJson = mapper.writeValueAsString(42);
final Integer deserializedVal = mapper.readValue(serializedJson,
Integer.class);
System.out.println(serializedJson);
System.out.println("Deserialized Value: " + deserializedVal);
Output:
{"Integer":42}
Deserialized Value: 42
See this answer for details on how to retrieve and configure your ObjectMapper instance in a JAX-RS environment.

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;
}
}

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

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

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

Jackson: how to treat {type:xx}?

I have the following json:
{"resourceWithType":
{"parentId":0,
"pluginId":0,
"pluginName":"Platforms",
"resourceId":10001,
"resourceName":"snert",
"typeId":10057,
"typeName":"Mac OS X"
}
}
And a class
public class ResourceWithType {
String resourceName;
int resourceId;
String typeName;
with all the getters and setters and so on.
The above JSON was actually created via RESTeasy and the Jettison provider where the class was marked with #XmlRootElement.
When I try to deserialize the above JSON via
ObjectMapper mapper=new ObjectMapper();
ResourceWithType rwt = mapper.readValue(json,ResourceWithType.class);
It fails with
06-13 11:07:55.360: WARN/System.err(26040):
org.codehaus.jackson.map.exc.UnrecognizedPropertyException:
Unrecognized field "resourceWithType"
(Class org.rhq.core.domain.rest.ResourceWithType),
not marked as ignorable
Which is sort of understandable.
How can I tell Jackson, that the embedded 'resourceWithType' is actually the class to deserialize into?
Other option would be to tell jettison not to include that type - how?
Tree model is a possibility; or just a simple wrapper like:
class {
public ResourceWithType resourceWithType;
}
to let you unwrap it. But often framework itself should handle unwrapping, since they are ones adding extra wrapping (Jackson does not add 'resourceWithType' in there by default).
Perhaps use the TreeModel API to unwrap the first (tag name) level, then deserialize the inner contents as usual (using the binding API)?