How to parse JSON's array of numbers - json

I'm facing the following situation parsing a JSON.
The JSON I want to unmarshal contains an array of numbers (doubles) like this:
"position":[52.50325,13.39062]
So there is no name/value pairs.
The Problem is that I can't get the value of this array. In the Java object modeling the JSON I defined the position attribute as list of Doubles: List<Double> but after the unmarshel, the position attribute is always null.
For testing purpose I changed the content of the JSON like that:
position: [„52.50325“ ,“ 13.39062“ ]
and then there is no issue, I get the list with two elements. (Btw, this happens regardless if the position is defined as list of Strings or list of Doubles (List<String> or List<Double>))
So a workaround could be to alter the JSON response and mark this numbers as string before unmarshaling it, but I would like to avoid that, and I’m wondering if there is solution to get the value of a number array?
Here is a snapshot from the code:
ResultsListDO.java:
#XmlElement(required = true)
protected List<Double> position;
public List<Double> getPosition()
{
if (position == null) {
position = new ArrayList<Double>();
}
return this.position;
}
JSON unmarshal:
context = new JSONJAXBContext(JSONConfiguration.mapped().build(), ResultsListDO.class);
JSONUnmarshaller um = context.createJSONUnmarshaller();
ResultsListDO resultsList = um.unmarshalFromJSON(source.getReader(), ResultsListDO.class);

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
Unless you have annotated your class with #XmlAccessorType(XmlAccessType.FIELD) the problem is probably that your annotation is on the field rather than the get method (see: http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html).
import java.util.*;
import javax.xml.bind.annotation.XmlElement;
public class Foo {
protected List<Double> position;
#XmlElement(required = true)
public List<Double> getPosition()
{
if (position == null) {
position = new ArrayList<Double>();
}
return this.position;
}
}
Demo Code
Below I'll demonstrate that everything works using MOXy as the JSON-binding provider.
jaxb.properties
To specify MOXy as your JAXB 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/2011/05/specifying-eclipselink-moxy-as-your.html).
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String,Object>();
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
JAXBContext jc = JAXBContext.newInstance(new Class[] {Foo.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StreamSource json = new StreamSource("src/forum18355753/input.json");
Foo foo = unmarshaller.unmarshal(json, Foo.class).getValue();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(foo, System.out);
}
}
input.json/Output
{
"position" : [ 52.50325, 13.39062 ]
}

Related

jaxb list with one element

I produce JSON with a List<> member inside. It is marshalled OK.
However, my consuming (third-)party complains about a missing []-pair, when the list has only one element. What I produce is like:
"mylist":{"id":104,"name":"Only one found"} // produced
while my consumer expects:
"mylist":[{"id":104,"name":"Only one found"}] // expected by third party
Is my implementation producing incorrect JSON?
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. The behaviour you are seeing is most likely due to a JAXB implementation being used with a library like Jettison. Jettison converts StAX events to/from JSON and can only detect a list when an element occurs more than once (see: http://blog.bdoughan.com/2011/04/jaxb-and-json-via-jettison.html). EclipseLink JAXB offers native JSON binding and can correctly represent arrays of size 1.
JAVA MODEL
Foo
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
private List<Bar> mylist;
}
Bar
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Bar {
private int id;
private String name;
}
jaxb.properties
To specify MOXy as your JAXB 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/2011/05/specifying-eclipselink-moxy-as-your.html):
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
DEMO CODE
Demo
import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(2);
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
JAXBContext jc = JAXBContext.newInstance(new Class[] {Foo.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StreamSource json = new StreamSource("src/forum15404528/input.json");
Foo foo = unmarshaller.unmarshal(json, Foo.class).getValue();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(foo, System.out);
}
}
input.json/Output
We see that the mylist is correctly represented as a JSON array.
{
"mylist" : [ {
"id" : 104,
"name" : "Only one found"
} ]
}
ADDITIONAL 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

Converting Java Array/Collection to JSON array

My Java code returns a Collection (ArrayList) and the resulting JSON produced by JAXB looks like:
{"todo":[{"name":"CAMPBELL","sales":"3","time":"1331662363931"},
{"name":"FRESNO","sales":"2","time":"1331662363931"}]}
But, is there a way I can make it look like:
[{"name":"CAMPBELL","sales":"3","time":"1331662363931"},
{"name":"FRESNO","sales":"2","time":"1331662363931"}]
Is there a way in Java/JAXB or maybe in AJAX callback using responseText. .
BTW, I've also tried with Java array but it made no difference.
Any help will be appreciated.
All you need is:
var todo = resp.todo;
where resp is the whole JSON response. Note that this is actually good design. A root array is not recommended due to JSON hijacking.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
Instead of correcting bad JSON on the client, you could fix the problem on the server. Below is an example of how EclipseLink JAXB (MOXy) could be used to produce the desired JSON:
Demo
package forum9689970;
import java.io.StringReader;
import java.util.List;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(SalesPerson.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setProperty("eclipselink.media-type", "application/json");
unmarshaller.setProperty("eclipselink.json.include-root", false);
String jsonString = "[{\"name\":\"CAMPBELL\",\"sales\":\"3\",\"time\":\"1331662363931\"},{\"name\":\"FRESNO\",\"sales\":\"2\",\"time\":\"1331662363931\"}]";
StreamSource json = new StreamSource(new StringReader(jsonString));
List<SalesPerson> salesPeople = (List<SalesPerson>) unmarshaller.unmarshal(json, SalesPerson.class).getValue();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("eclipselink.media-type", "application/json");
marshaller.setProperty("eclipselink.json.include-root", false);
marshaller.marshal(salesPeople, System.out);
}
}
SalesPerson
package forum9689970;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class SalesPerson {
private String name;
private String sales;
private String time;
}
Output
[ {
"name" : "CAMPBELL",
"sales" : "3",
"time" : "1331662363931"
}, {
"name" : "FRESNO",
"sales" : "2",
"time" : "1331662363931"
} ]
For More Information
MOXy as Your JAX-RS JSON Provider - Server Side
JSON Binding with EclipseLink MOXy - Twitter Example
Specifying EclipseLink MOXy as Your JAXB Provider

Is there a possibility to hide the "#type" entry when marshalling subclasses to JSON using EclipseLink MOXy (JAXB)?

I'm about to develop a JAX-RS based RESTful web service and I use MOXy (JAXB) in order to automatically generate my web service's JSON responses.
Everything is cool, but due to the fact that the web service will be the back-end of a JavaScript-based web application and therefore publicly accessible I don't want to expose certain details like class names, etc.
But, I've realized that under certain conditions MOXy embeds a "#type" entry into the marshalled string and this entry is followed by the class name of the object that has just been marshalled.
In particular, I've realized that MOXy behaves in this way when marshalling instances of extended classes.
Assume the following super class "MyBasicResponse"
#XmlRootElement(name="res")
public class MyBasicResponse {
#XmlElement
private String msg;
public MyBasicResponse() {
// Just for conformity
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
And this specialized (extended) class "MySpecialResponse"
#XmlRootElement(name="res")
public class MySpecialResponse extends MyBasicResponse {
#XmlElement
private String moreInfo;
public MySpecialResponse() {
// Just for conformity
}
public String getMoreInfo() {
return moreInfo;
}
public void setMoreInfo(String moreInfo) {
this.moreInfo = moreInfo;
}
}
So, the MyBasicResponse object's marshalled string is
{"msg":"A Message."}
(That's okay!)
But, the MySpecialResponse object's marshalled string is like
{"#type":"MySpecialResponse","msg":"A Message.","moreInfo":"More Information."}
Is there a way to strip the
"#type":"MySpecialResponse"
out of my response?
You can wrap your object in an instance of JAXBElement specifying the subclass being marshalled to get rid of the type key. Below is a full example.
Java Model
Same as from the question, but with the following package-info class added to specifying the field access to match those classes
#XmlAccessorType(XmlAccessType.FIELD)
package com.example.foo;
import javax.xml.bind.annotation.*;
Demo Code
Demo
import java.util.*;
import javax.xml.bind.*;
import javax.xml.namespace.QName;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(2);
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
JAXBContext jc = JAXBContext.newInstance(new Class[] {MySpecialResponse.class}, properties);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
MySpecialResponse msr = new MySpecialResponse();
marshaller.marshal(msr, System.out);
JAXBElement<MySpecialResponse> jaxbElement = new JAXBElement(new QName(""), MySpecialResponse.class, msr);
marshaller.marshal(jaxbElement, System.out);
}
}
Output
We see that when the object was marshalled an type key was marshalled (corresponding to the xsi:type attribute in the XML representation), because as MOXy is concerned it was necessary to distinguish between MyBasicResponse and MySpecialResponse. When we wrapped the object in an instance of JAXBElement and qualified the type MOXy didn't need to add the type key.
{
"type" : "mySpecialResponse"
}
{
}
For More Information
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html

GSON deserialization problem

I am having a deserialization problem using the GSON library.
The following is the JSON code which I try to deserialize
{"response": {
"#service": "CreateUser",
"#response-code": "100",
"#timestamp": "2010-11-27T15:52:43-08:00",
"#version": "1.0",
"error-message": "",
"responseData": {
"user-guid": "023804207971199"
}
}}
I create the following classes
public class GsonContainer {
private GsonResponse mResponse;
public GsonContainer() { }
//get & set methods
}
public class GsonResponse {
private String mService;
private String mResponseCode;
private String mTimeStamp;
private String mVersion;
private String mErrorMessage;
private GsonResponseCreateUser mResponseData;
public GsonResponse(){
}
//gets and sets method
}
public class GsonResponseCreateUser {
private String mUserGuid;
public GsonResponseCreateUser(){
}
//get and set methods
}
After calling the GSON library the data is null. Any ideas what is wrong with the classes?
Thx in advance for your help ... I assume it's something trivial ....
#user523392 said:
the member variables have to match exactly what is given in the JSON response
This is not the case.
There are a few options for specifying how Java field names map to JSON element names.
One solution that would work for the case in the original question above is to annotate the Java class members with the #SerializedName to very explicitly declare what JSON element name it maps to.
// output: [MyObject: element=value1, elementTwo=value2]
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
public class Foo
{
static String jsonInput =
"{" +
"\"element\":\"value1\"," +
"\"#element-two\":\"value2\"" +
"}";
public static void main(String[] args)
{
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
MyObject object = gson.fromJson(jsonInput, MyObject.class);
System.out.println(object);
}
}
class MyObject
{
String element;
#SerializedName("#element-two")
String elementTwo;
#Override
public String toString()
{
return String.format(
"[MyObject: element=%s, elementTwo=%s]",
element, elementTwo);
}
}
Another approach is to create a custom FieldNamingStrategy to specify how Java member names are translated to JSON element names. This example would apply the same name mapping to all Java member names. This approach would not work for the original example above, because not all of the JSON element names follow the same naming pattern -- they don't all start with '#' and some use camel case naming instead of separating name parts with '-'. An instance of this FieldNamingStrategy would be used when building the Gson instance (gsonBuilder.setFieldNamingStrategy(new MyFieldNamingStrategy());).
class MyFieldNamingStrategy implements FieldNamingStrategy
{
// Translates the field name into its JSON field name representation.
#Override
public String translateName(Field field)
{
String name = field.getName();
StringBuilder translation = new StringBuilder();
translation.append('#');
for (int i = 0, length = name.length(); i < length; i++)
{
char c = name.charAt(i);
if (Character.isUpperCase(c))
{
translation.append('-');
c = Character.toLowerCase(c);
}
translation.append(c);
}
return translation.toString();
}
}
Another approach to manage how Java field names map to JSON element names is to specify a FieldNamingPolicy when building the Gson instance, e.g., gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES);. This also would not work with the original example, however, since it applies the same name mapping policy to all situations.
The JSON response above cannot be deserialized by GSON because of the special characters # and -. GSON is based on reflections and the member variables have to match exactly what is given in the JSON response.

Jersey / JAXB: Unmarshaling of empty json array results in a list with one item where all fields are set to null

I have a really simple rest web service returning a list of questions. This code works as expected when the number of questions returned are greater than zero. But if the server returns an empty json array like [], JAXB creates a list with one question instance where all fields are set to null!
I'm new to both Jersey and JAXB so I don't know whether I haven't configured it correctly or whether this is a known problem. Any tips?
Client configuration:
DefaultApacheHttpClientConfig config = new DefaultApacheHttpClientConfig();
config.getProperties().put(DefaultApacheHttpClientConfig.PROPERTY_HANDLE_COOKIES, true);
config.getClasses().add(JAXBContextResolver.class);
//config.getClasses().add(JacksonJsonProvider.class); // <- Jackson causes other problems
client = ApacheHttpClient.create(config);
JAXBContextResolver:
#Provider
public final class JAXBContextResolver implements ContextResolver<JAXBContext> {
private final JAXBContext context;
private final Set<Class> types;
private final Class[] cTypes = { Question.class };
public JAXBContextResolver() throws Exception {
this.types = new HashSet(Arrays.asList(cTypes));
this.context = new JSONJAXBContext(JSONConfiguration.natural().build(), cTypes);
}
#Override
public JAXBContext getContext(Class<?> objectType) {
return (types.contains(objectType)) ? context : null;
}
}
Client code:
public List<Question> getQuestionsByGroupId(int id) {
return digiRest.path("/questions/byGroupId/" + id).get(new GenericType<List<Question>>() {});
}
The Question class is just a simple pojo.
I know this is not exactly an answer to your question, but I choosed to use GSON on top of jersey, for my current projects. (and I try to avoid JAXB as much as possible), and I found it very easy and resilient.
You just have to declare
#Consumes(MediaType.TEXT_PLAIN)
or
#Produces(MediaType.TEXT_PLAIN)
or both, and use the GSON marshaller/unmarshaller, and work with plain Strings. Very easy to debug, unittest too...
Using Jackson may help.
See org.codehaus.jackson.map.ObjectMapper and org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion.NON_EMPTY
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize;
public class SampleContextResolver implements ContextResolver<ObjectMapper>
{
#Override
public ObjectMapper getContext(Class<?> type)
{
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationConfig(mapper.getSerializationConfig()
.withSerializationInclusion(JsonSerialize.Inclusion.NON_EMPTY)
}
}