Google's Gson.toJson(Object src) not converting boolean values.
For example, My Java object is say:
class MyObj {
private String name;
private boolean teen;
//getter,setter ommitted...
}
MyObj obj = new MyObj();
obj.setName("Me");
obj.isTeen(false);
Gson.toJson(obj);
While converting this object it's writing like this
MyObj { "name" : "Me" }
boolean is missing even though there was a value.
Gson doesn't serialize the fields only if the field is defined as transient or static.
If you haven't defined the boolean field as static or transient, json should have that field.
I am not sure how you are setting the value to boolean field using isTeen() method. It should give compilation error unless you have non-trivial "is" method for boolean field. The "is" method is basically an equivalent of "get" method for String/other fields. Typically, it won't be used to set the value.
I have just provided the full code with getters and setters. Please check whether this works.
public class MyObj {
private String name;
private boolean teen;
public String getName() {
return name;
}
public boolean isTeen() {
return teen;
}
public void setName(String name) {
this.name = name;
}
public void setTeen(boolean teen) {
this.teen = teen;
}
}
Main Method:-
public static void main(String[] args) {
Gson gson = new Gson();
MyObj obj = new MyObj();
obj.setName("Me");
obj.setTeen(false);
System.out.println(gson.toJson(obj));
}
Output:-
{"name":"Me","teen":false}
The interesting point is that even if you don't set any value for boolean field. It will take the default as false and the generated JSON will have false.
Related
{
"cust":"A",
"del":[{
"type1": "id",
"type2":[{
"name":"address"
}]
}]
I have converted this json to below model class
public class Del{
public String type1;
public JSONArray type2; // "type2" has dynamic key and value which are string, it can have "name":"address","id":"sal" pair etc dynamically
}
public class Root{
public String cust;
public List<Del> del;
}
But, this mapping is showing error. how to fetch key and value inside "type2" in dynamic way where attribute names are not fixed.
If you have a list, you need a List<T>. What is T? If it can have any number of keys, then it must be a Map<K, V>. K is String, and V is either String or Object, depending on your actual structure. If you have a handful of expected keys and want to ignore everything else, make T a custom class of yours:
class Type2Props {
public String name;
public String address;
public String id;
public String sal;
}
Properties/fields without a value in the JSON will be left initialized as null.
Putting it together, you either need
public class Del {
public String type1;
public List<Type2Props> type2;
}
or
public class Del {
public String type1;
public List<Map<String, String>> type2; /* might be `Map<String, Object>` */
}
I am working on an embedded jersey instance which will run a JAXB RESTful service. I have configured Jackson with two steps:
Adding this to my POM
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.23.2</version>
</dependency>
Registering it in my application
public HandheldApplication() {
scripts.add(HandheldServer.class);
scripts.add(BasicScript.class);
// Add JacksonFeature.
scripts.add(JacksonFeature.class);
scripts.add(LoggingFilter.class);
}
I have a complex object being passed back and forth as shown below:
package com.ziath.handheldserver.valueobjects;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.*;
#SuppressWarnings("restriction")
#XmlRootElement
public class Widget {
private String key;
private String name;
private List<String> options = new ArrayList<String>();
private String value;
private String type;
public Widget(){
super();
}
public Widget(String key, String name, List<String> options, String value,
String type) {
super();
this.key = key;
this.name = name;
this.options = options;
this.value = value;
this.type = type;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getOptions() {
return options;
}
public void setOptions(List<String> options) {
this.options = options;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
When I execute this in a GET method as shown below:
#Override
#GET
#Path("getKeys")
#Produces(MediaType.APPLICATION_JSON)
public List<Widget> getKeys(#QueryParam(value = "page") int page)
This works fine and I get JSON back; however when I execute it is a PUT as shown below:
#Override
#PUT
#Path("validateKeys")
#Produces({MediaType.APPLICATION_JSON})
#Consumes(MediaType.APPLICATION_JSON)
public boolean validateKeys(#QueryParam(value = "page")int page, #QueryParam(value = "widgets")List<Widget> widgets)
When I execute a PUT to access this method I get a stack trace as follows:
Caused by: org.glassfish.jersey.internal.inject.ExtractorException: Error un-marshalling JAXB object of type: class com.ziath.handheldserver.valueobjects.Widget.
at org.glassfish.jersey.jaxb.internal.JaxbStringReaderProvider$RootElementProvider$1.fromString(JaxbStringReaderProvider.java:195)
at org.glassfish.jersey.server.internal.inject.AbstractParamValueExtractor.convert(AbstractParamValueExtractor.java:139)
at org.glassfish.jersey.server.internal.inject.AbstractParamValueExtractor.fromString(AbstractParamValueExtractor.java:130)
at org.glassfish.jersey.server.internal.inject.CollectionExtractor.extract(CollectionExtractor.java:88)
at org.glassfish.jersey.server.internal.inject.CollectionExtractor$ListValueOf.extract(CollectionExtractor.java:107)
at org.glassfish.jersey.server.internal.inject.QueryParamValueFactoryProvider$QueryParamValueFactory.provide(QueryParamValueFactoryProvider.java:89)
... 38 more
Caused by: javax.xml.bind.UnmarshalException
- with linked exception:
[org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.]
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.createUnmarshalException(AbstractUnmarshallerImpl.java:335)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.createUnmarshalException(UnmarshallerImpl.java:563)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:249)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:214)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:140)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:123)
at org.glassfish.jersey.jaxb.internal.JaxbStringReaderProvider$RootElementProvider$1.fromString(JaxbStringReaderProvider.java:190)
... 43 more
So it seems to me that Jackson is correctly marshalling my POJO into JSON but trying to unmarshall it as XML. Note that I switched to Jackson away from MOXy because I needed to be able to handle collections coming back and forth and apparently MOXy cannot do that.
Is there a setting I've missed to tell Jackson/Jersey to go both ways for JSON?
Try removing #QueryParam(value = "widgets") because you should pass it as entity body - not query param.
#PUT
#Path("validateKeys")
#Produces({MediaType.APPLICATION_JSON})
#Consumes(MediaType.APPLICATION_JSON)
public boolean validateKeys(#QueryParam(value = "page")int page, List<Widget> widgets)
Also you can make wrapper class:
#XmlRootElement
public class Widgets {
private List<Widget> widgets;
// other fields, setters and getters
}
And then:
#PUT
#Path("validateKeys")
#Produces({MediaType.APPLICATION_JSON})
#Consumes(MediaType.APPLICATION_JSON)
public boolean validateKeys(#QueryParam(value = "page")int page, Widgets widgets)
I would suggest to read some discussions about REST design because you're using verbs in your paths:
Is this a bad REST URL?
Understanding REST: Verbs, error codes, and authentication
I was switching between QueryParam and FormParam to try and get one of them to work. If I use FormParam I also need to change the consumes to APPLICATION_FORM_URLENCODED.
The actual issue was that the default unmarshalling with Jackson was using XML because it was tagged as an XML resource - take that out! I finally managed to work out how to unmarshall from JSON by using a static fromString method. Then to handle the list; I cannot use a wrapper class because this needs to be highly cross language and exposing a wrapper with a list would have complicated the implementation from Python, C#, etc. The way to get it to accept a list with a wrapper is to post the name of the param (in this case widgets) multiple time. Then each JSON passed in will be called against the fromString method.
Here is an example:
class Person {
String name;
Address addressGiven;
//getters and setters
class Address {
#JsonProperty(name="stno")
private String StreetNo
#JsonProperty(name="type")
private AddressType addType;
public void setstno(String stno){
if (this.addressGiven==null)
addressGiven=new Address();
addressGiven.setStno(stno);
}
public void setType(String type) {
if (addressGiven==null){
addressGiven=new Address();
}
addressGiven.setType(AddressType.valueOf(type));
}
// other getters and setters
}
}
AddressType.java
Enum AddressType {
HOME,
OFFICE,
BUSINESS,
DEFAULT;
}
Two points to note before I go to my question:
Address in an inner class
the instance attribute addType is of enum type
when I serialize the object:
Person person = new Person();
Person.setStNo("1234");
person.setType("HOME");
ObjectMapper mapper = new ObjectMapper();
String body = mapper.writeValueAsString(person);
System.out.println(body);
I expect:
{
"addressGiven:
{ "stno" : "1234",
"type" : HOME,
}
}
but what I get is this :
{ "streetNo" : "1234"}.
Three noticable differences
nested json is missing
streetNo but not stno is returned
No addressType is present.
why is the expected json (i.e inner not returned. am I missing some annotations anywhere?
I browsed through jackson docs. but could not figure out sooner. so here I am?
Jackson will automatically call the empty constructor on the object is serializing. the exception being if a constructor is annotated with #JsonCreator, or a builder class annotated with #JsonPOJOBuilder, and maybe another one im missing. i would remove the creation of Address and also the checking for null. dummy down those setters/getters.
ObjecMapper by default handles serialization of an Enum. i would suggest removing that manual conversion
#see DeserializationFeature.READ_ENUMS_USING_TO_STRING. default value is false which means that it uses Enum.valueOf to serialize the String into the correct value.
with all that being said, you are expecting something that doesnt match your code. Person does not have an attribute type, nor stNo. those are Address attributes. im curious to know how you get the output shown. see below for code and example output
class Person {
private String name;
private Address addressGiven;
public void setName(String name) { this.name = name; }
public void setAddressGiven(Address addressGiven) { this.addressGiven = addressGiven; }
public String getName() { return name; }
public Address getAddressGiven() { return addressGiven; }
enum AddressType { HOME, OFFICE, BUSINESS, DEFAULT }
static class Address {
#JsonProperty("stno") private String streetNo;
#JsonProperty("type") private AddressType addType;
public String getStreetNo() { return streetNo; }
public void setStreetNo(String streetNo) { this.streetNo = streetNo; }
public AddressType getAddType() { return addType; }
public void setAddType(AddressType addType) { this.addType = addType;}
}
public static void main(String[] args) throws JsonProcessingException {
Person person = new Person();
person.name = "joe";
Address address = new Address();
address.addType = AddressType.BUSINESS;
address.streetNo = "010101";
person.addressGiven = address;
ObjectMapper mapper = new ObjectMapper();
String body = mapper.writeValueAsString(person);
System.out.println(body);
}
}
{"name":"joe","addressGiven":{"stno":"010101","type":"BUSINESS"}}
Is it possible to serialize an object using toJson(object) and have the toJson-parser ignore certain methods?
We have a method in a User class (getSocial - which is concerned with Facebook integration) that makes the toJson()-parsing fail - and we'd like it go ignore that method when serializing if possible.
Can this be done?
You can just iterate the object and rewrite it to Map or List with specified values only.
Note that if you are choosing your objects with Ebean it fetches whole object, also data, which shouldn't be fetched (as password or other credentials)
use fastjson & PropertyFilter:
Sample Code
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.PropertyFilter;
import com.alibaba.fastjson.serializer.SerializeWriter;
PropertyFilter filter = new PropertyFilter() {
public boolean apply(Object source, String name, Object value) {
return false;
}
};
SerializeWriter out = new SerializeWriter();
JSONSerializer serializer = new JSONSerializer(out);
serializer.getPropertyFilters().add(filter);
A a = new A();
serializer.write(a);
String text = out.toString();
Assert.assertEquals("{}", text);
PropertyFilter
PropertyFilter filter = new PropertyFilter() {
public boolean apply(Object source, String name, Object value) {
if("name".equals(name)) {
return true;
}
return false;
}
};
SerializeWriter out = new SerializeWriter();
JSONSerializer serializer = new JSONSerializer(out);
serializer.getPropertyFilters().add(filter);
A a = new A();
a.setName("chennp2008");
serializer.write(a);
String text = out.toString();
Assert.assertEquals("{\"name\":\"chennp2008\"}", text);
ValueObject:
public static class A {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Good day,
I am currently integration attempting to consume a REST service that produces JSON (written in .NET) using Jackson (with Jersey). The JSON consists of a possible error message and an array of objects. Below is a sample of the JSON returned as produced by Jersey's logging filter:
{
"error":null,
"object":"[{\"Id\":16,\"Class\":\"ReportType\",\"ClassID\":\"4\",\"ListItemParent_ID\":4,\"Item\":\"Pothole\",\"Description\":\"Pothole\",\"Sequence\":1,\"LastEditDate\":null,\"LastEditor\":null,\"ItemStatus\":\"Active\",\"ItemColor\":\"#00AF64\"}]"
}
I have two classes to represent the type (the outer ListResponse):
public class ListResponse {
public String error;
public ArrayList<ListItem> object;
public ListResponse() {
}
}
and (the inner ListItem):
public class ListItem {
#JsonProperty("Id")
public int id;
#JsonProperty("Class")
public String classType;
#JsonProperty("ClassID")
public String classId;
#JsonProperty("ListItemParent_ID")
public int parentId;
#JsonProperty("Item")
public String item;
#JsonProperty("Description")
public String description;
#JsonAnySetter
public void handleUnknown(String key, Object value) {}
public ListItem() {
}
}
The class that invokes and returns the JSON looks like this:
public class CitizenPlusService {
private Client client = null;
private WebResource service = null;
public CitizenPlusService() {
initializeService("http://localhost:59105/PlusService/");
}
private void initializeService(String baseURI) {
// Use the default client configuration.
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getClasses().add(JacksonJsonProvider.class);
client = Client.create(clientConfig);
// Add a logging filter to track communication between server and client.
client.addFilter(new LoggingFilter());
// Add the base URI
service = client.resource(UriBuilder.fromUri(baseURI).build());
}
public ListResponse getListItems(String id) throws Exception
{
ListResponse response = service.path("GetListItems").path(id).accept(MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_XML_TYPE).get(ListResponse.class);
return response;
}
}
The important call here is the getListItems method. Running the code in a test harness, produces the following:
org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token
at [Source: java.io.StringReader#49497eb8; line: 1, column: 14] (through reference chain: citizenplus.types.ListResponse["object"])
Please assist.
Regards,
Carl-Peter Meyer
You may be missing a #JsonDeserialize attribute as the type information does get lost in generics at run-time. Also you should avoid using concrete classes for collections if you can.
public class ListResponse {
public String error;
#JsonDeserialize(as=ArrayList.class, contentAs=ListItem.class)
public List<ListItem> object;
}
Your problem is that the 'object' property value is a String and not an array! The string contains a JSON array but Jackson expects a native array (without the wrapping quotes).
I had the same problem and I created a custom deserializer, which will deserialize a string value to a generic collection of the desired type:
public class JsonCollectionDeserializer extends StdDeserializer<Object> implements ContextualDeserializer {
private final BeanProperty property;
/**
* Default constructor needed by Jackson to be able to call 'createContextual'.
* Beware, that the object created here will cause a NPE when used for deserializing!
*/
public JsonCollectionDeserializer() {
super(Collection.class);
this.property = null;
}
/**
* Constructor for the actual object to be used for deserializing.
*
* #param property this is the property/field which is to be serialized
*/
private JsonCollectionDeserializer(BeanProperty property) {
super(property.getType());
this.property = property;
}
#Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
return new JsonCollectionDeserializer(property);
}
#Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
switch (jp.getCurrentToken()) {
case VALUE_STRING:
// value is a string but we want it to be something else: unescape the string and convert it
return JacksonUtil.MAPPER.readValue(StringUtil.unescapeXml(jp.getText()), property.getType());
default:
// continue as normal: find the correct deserializer for the type and call it
return ctxt.findContextualValueDeserializer(property.getType(), property).deserialize(jp, ctxt);
}
}
}
Note that this deserializer will also work if the value actually is an array and not a string, because it delegates the actual deserialization accordingly.
In your example you would now have to annotate your collection field like so:
public class ListResponse {
public String error;
#JsonDeserialize(using = JsonCollectionDeserializer.class)
public ArrayList<ListItem> object;
public ListResponse() {}
}
And that should be it.
Note: JacksonUtil and StringUtil are custom classes, but you can easily replace them. For example by using new ObjectMapper() and org.apache.commons.lang3.StringEscapeUtils.
The register subTypes works!
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
public interface Geometry {
}
public class Point implements Geometry{
private String type="Point";
....
}
public class Polygon implements Geometry{
private String type="Polygon";
....
}
public class LineString implements Geometry{
private String type="LineString";
....
}
GeoJson geojson= null;
ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.registerSubtypes(Polygon.class,LineString.class,Point.class);
try {
geojson=mapper.readValue(source, GeoJson.class);
} catch (IOException e) {
e.printStackTrace();
}