deserializing generics with gson - json

I am using GSON 1.4 and serializing an object with two generic arraylist<myObject> as follows
String data = Gson.toJson(object, object.class). When I desirialize it I do gson.fromJson(json, type);
sadly I get
java.lang.IllegalArgumentException: Can not set java.util.ArrayList
field ... to java.util.LinkedList
Why is that ? GSON doc notes that if I serialize with object.class parameter it supports generics. any idea? thanks.
my class is :
public class IndicesAndWeightsParams {
public List<IndexParams> indicesParams;
public List<WeightParams> weightsParams;
public IndicesAndWeightsParams() {
indicesParams = new ArrayList<IndexParams>();
weightsParams = new ArrayList<WeightParams>();
}
public IndicesAndWeightsParams(ArrayList<IndexParams> indicesParams, ArrayList<WeightParams> weightsParams) {
this.indicesParams = indicesParams;
this.weightsParams = weightsParams;
}
}
public class IndexParams {
public IndexParams() {
}
public IndexParams(String key, float value, String name) {
this.key = key;
this.value = value;
this.name = name;
}
public String key;
public float value;
public String name;
}

Gson has some limitations regarding collections because of Java's type erasure. You can read more about it here.
From your question I see you're using both ArrayList and LinkedList. Are you sure you didn't mean to use just List, the interface?
This code works:
List<String> listOfStrings = new ArrayList<String>();
listOfStrings.add("one");
listOfStrings.add("two");
Gson gson = new Gson();
String json = gson.toJson(listOfStrings);
System.out.println(json);
Type type = new TypeToken<Collection<String>>(){}.getType();
List<String> fromJson = gson.fromJson(json, type);
System.out.println(fromJson);
Update: I changed your class to this, so I don't have to mess around with other classes:
class IndicesAndWeightsParams {
public List<Integer> indicesParams;
public List<String> weightsParams;
public IndicesAndWeightsParams() {
indicesParams = new ArrayList<Integer>();
weightsParams = new ArrayList<String>();
}
public IndicesAndWeightsParams(ArrayList<Integer> indicesParams, ArrayList<String> weightsParams) {
this.indicesParams = indicesParams;
this.weightsParams = weightsParams;
}
}
And using this code, everything works for me:
ArrayList<Integer> indices = new ArrayList<Integer>();
ArrayList<String> weights = new ArrayList<String>();
indices.add(2);
indices.add(5);
weights.add("fifty");
weights.add("twenty");
IndicesAndWeightsParams iaw = new IndicesAndWeightsParams(indices, weights);
Gson gson = new Gson();
String string = gson.toJson(iaw);
System.out.println(string);
IndicesAndWeightsParams fromJson = gson.fromJson(string, IndicesAndWeightsParams.class);
System.out.println(fromJson.indicesParams);
System.out.println(fromJson.weightsParams);

Related

How to get inner JSON object with GSON/Retrofit when outer name is variable?

I have a JSON response from an API like this:
{"asalas77":
{"id":23519033,"name":"Asalas77","profileIconId":22,"revisionDate":1487214366000,"summonerLevel":30}
}
And I need to extract the inner object from it. I tried using a deserializer like shown in this question Get nested JSON object with GSON using retrofit but it doesn't work for me.
public class SummonerDeserializer implements JsonDeserializer<Summoner> {
#Override
public Summoner deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException {
long id = je.getAsJsonObject().get("id").getAsLong();
String name = je.getAsJsonObject().get("name").getAsString();
int profileIconId = je.getAsJsonObject().get("profileIconId").getAsInt();
long revisionDate = je.getAsJsonObject().get("revisionDate").getAsLong();
long summonerLevel = je.getAsJsonObject().get("summonerLevel").getAsLong();
Summoner s = new Summoner();
s.setId(id);
s.setName(name);
s.setProfileIconId(profileIconId);
s.setRevisionDate(revisionDate);
s.setSummonerLevel(summonerLevel);
return s;
}
}
But the problem is I can't access the inner fields from JsonElement je and the name asalas77 is a variable (it's a search query) so I can't extract the inner object directly.
You must have a wrapper class in order not to clash deserialization strategies. Assume it's as follows:
final class SummonerResponse {
private final Summoner summoner;
private SummonerResponse(final Summoner summoner) {
this.summoner = summoner;
}
static SummonerResponse summonerResponse(final Summoner summoner) {
return new SummonerResponse(summoner);
}
Summoner getSummoner() {
return summoner;
}
}
Then you can either create a custom response deserializer:
final class SummonerWrapperDeserializer
implements JsonDeserializer<SummonerResponse> {
private static final JsonDeserializer<SummonerResponse> summonerDeserializer = new SummonerWrapperDeserializer();
private SummonerWrapperDeserializer() {
}
static JsonDeserializer<SummonerResponse> getSummonerResponseDeserializer() {
return summonerDeserializer;
}
#Override
public SummonerResponse deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
throws JsonParseException {
// Pick the root as a JSON object
final JsonObject outerJsonObject = jsonElement.getAsJsonObject();
// And check how many properties does it have
final Iterable<? extends Entry<String, JsonElement>> outerJsonObjectEntries = outerJsonObject.entrySet();
if ( outerJsonObject.size() != 1 ) {
throw new JsonParseException("Expected one property object, the actual properties are: " + getPropertyName(outerJsonObjectEntries));
}
// If it has only one property, just get the property and take its inner value
final JsonElement innerJsonElement = outerJsonObjectEntries.iterator().next().getValue();
// Once it's obtained, just delegate the parsing to a downstream parser - no need to create Summoner instances by hands
return summonerResponse(context.deserialize(innerJsonElement, Summoner.class));
}
private static Set<String> getPropertyName(final Iterable<? extends Entry<String, JsonElement>> entries) {
final Set<String> keys = new LinkedHashSet<>();
for ( final Entry<String, JsonElement> entry : entries ) {
keys.add(entry.getKey());
}
return keys;
}
}
Or save some memory (the JSON (de)serializers require some memory because they work with JSON trees) and create a more low level type adapter:
final class SummonerResponseTypeAdapterFactory
implements TypeAdapterFactory {
private static final TypeAdapterFactory summonerResponseTypeAdapterFactory = new SummonerResponseTypeAdapterFactory();
private SummonerResponseTypeAdapterFactory() {
}
static TypeAdapterFactory getSummonerResponseTypeAdapterFactory() {
return summonerResponseTypeAdapterFactory;
}
#Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
// Check if we can handle SummonerResponse. Classes can be compared with `==`
if ( typeToken.getRawType() == SummonerResponse.class ) {
final TypeAdapter<SummonerResponse> typeAdapter = getSummonerResponseTypeAdapter(gson);
#SuppressWarnings("unchecked")
final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) typeAdapter;
return castTypeAdapter;
}
return null;
}
}
final class SummonerResponseTypeAdapter
extends TypeAdapter<SummonerResponse> {
private final Gson gson;
private SummonerResponseTypeAdapter(final Gson gson) {
this.gson = gson;
}
static TypeAdapter<SummonerResponse> getSummonerResponseTypeAdapter(final Gson gson) {
return new SummonerResponseTypeAdapter(gson);
}
#Override
#SuppressWarnings("resource")
public void write(final JsonWriter out, final SummonerResponse summonerResponse)
throws IOException {
// The incoming object may be null
if ( summonerResponse == null && gson.serializeNulls() ) {
out.nullValue();
return;
}
// Generate the inner object
out.beginObject();
out.name(summonerResponse.getSummoner().name);
gson.toJson(summonerResponse.getSummoner(), Summoner.class, out);
out.endObject();
}
#Override
public SummonerResponse read(final JsonReader in)
throws IOException {
// is it a null?
if ( in.peek() == NULL ) {
return null;
}
// make sure that the inner read JSON contains an inner object
in.beginObject();
// ignore the name
in.nextName();
// delegate parsing to the backing Gson instance in order to apply downstream parsing
final Summoner summoner = gson.fromJson(in, Summoner.class);
// check if there are more properties within the inner object
if ( in.peek() == NAME ) {
throw new MalformedJsonException("Unexpected: " + in.nextName());
}
// consume the "}" token
in.endObject();
return summonerResponse(summoner);
}
}
Then any of the options above can be used like this:
final Gson gson = new GsonBuilder()
.registerTypeAdapter(SummonerResponse.class, getSummonerResponseDeserializer())
.create();
final SummonerResponse summonerResponse = gson.fromJson(JSON, SummonerResponse.class);
final Summoner summoner = summonerResponse.getSummoner();
out.println(summoner.id + " => " + summoner.name);
or
final Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(getSummonerResponseTypeAdapterFactory())
.create();
final SummonerResponse summonerResponse = gson.fromJson(JSON, SummonerResponse.class);
final Summoner summoner = summonerResponse.getSummoner();
out.println(summoner.id + " => " + summoner.name);
out.println(gson.toJson(summonerResponse));
The outputs are
23519033 => Asalas77
and
23519033 => Asalas77
{"Asalas77":{"id":23519033,"name":"Asalas77","profileIconId":22,"revisionDate":1487214366000,"summonerLevel":30}}
respectively.

How to parse JSON response from DuckDuckGo Answers API using Retrofit2?

I'm trying to obtain POJO instances using Gson and Retrofit2.
A typical JSON response looks like this.
My issue is with the Infobox field. In some cases, (like this) the field would be an object of the following type and an empty string otherwise.
class Infobox {
public List<Content> content = new ArrayList<>();
public List<Metum> meta;
}
class Content {
public String dataType;
public String value;
public String label;
public Integer wikiOrder;
}
class Metum {
public String dataType;
public String value;
public String label;
}
I tried writing a TypeAdapter as below
class InfoboxAdapter extends TypeAdapter<Infobox> {
final Gson embedded = new Gson();
#Override
public void write(JsonWriter out, Infobox infobox) throws IOException {
if (infobox == null) {
out.nullValue();
return;
}
out.beginObject();
out.name("content");
embedded.toJson(embedded.toJsonTree(infobox.content), out);
out.name("meta");
embedded.toJson(embedded.toJsonTree(infobox.meta), out);
out.endObject();
}
#Override
public Infobox read(JsonReader in) throws IOException {
if ("".equals(in.peek())) {
return null;
}
return embedded.fromJson(in, Infobox.class);
}
But it fails with java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
The more confusing fact is that the field meta in the response, which is also an object, will in some cases have the value as null (and not an empty string like infobox)
I'd prefer to be able to do it using Gson as I've used it for everything else and I don't want to add another dependency
Hi Please go to :http://www.jsonschema2pojo.org/
paste your code. this sites automatically create your all related classes.
if issue please have a look at this link.
my drive link
I ended up using a JsonDeserializer. Google recommends:
New applications should prefer TypeAdapter, whose streaming API is more efficient than this interface's tree API.
But I didn't notice any performance impact for my use. I might someday rewrite this to use a TypeAdapter, but this works for me in till then
class InfoboxDeserialiser implements JsonDeserializer<Infobox> {
#Override
public Infobox deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
try {
if (json.isJsonNull() || json.isJsonPrimitive()) {
return null;
}
JsonObject jsonObject = json.getAsJsonObject();
Infobox infobox = new Infobox();
JsonArray jsonContent = jsonObject.get("content").getAsJsonArray();
JsonArray jsonMeta = jsonObject.get("meta").getAsJsonArray();
infobox.content = new Content[jsonContent.size()];
for (int i = 0; i < jsonContent.size(); i++) {
infobox.content[i] = context.deserialize(jsonContent.get(i), Content.class);
}
infobox.meta = new Metum[jsonMeta.size()];
for (int i = 0; i < jsonMeta.size(); i++) {
infobox.meta[i] = context.deserialize(jsonContent.get(i), Metum.class);
}
return infobox;
} catch (Exception e) {
Timber.e(e, "Failed to deserialise the infobox");
return null;
}
}
}
Where the classes are as follows
class Metum {
public String dataType;
public String value;
public String label;
}
class Content {
public String dataType;
public String value;
public String label;
public Integer wikiOrder;
}
I register this deserializer while creating the service object
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Infobox.class, new InfoboxDeserialiser());
GsonConverterFactory converterFactory = GsonConverterFactory.create(gsonBuilder.create());
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl("https://api.duckduckgo.com/")
.addConverterFactory(converterFactory);

How to include only specific properties when serializing with Jackson

I am trying to implement a universal method which serializes the given object to JSON, but only those properties which are passed in a collection. If possible I want to get this functionality without specifying #JsonFilter on the class. For this I am trying to use FilterExceptFilter from Jackson 2.4.1. Dependencies:
jackson-core-2.4.1.jar
jackson-databind-2.4.1.jar
jackson-annotations-2.4.0.jar
Here is what I have at the moment:
public static String serializeOnlyGivenFields(Object o,
Collection<String> fields) throws JsonProcessingException {
if ((fields == null) || fields.isEmpty()) return null;
Set<String> properties = new HashSet<String>(fields);
SimpleBeanPropertyFilter filter =
new SimpleBeanPropertyFilter.FilterExceptFilter(properties);
SimpleFilterProvider fProvider = new SimpleFilterProvider();
fProvider.addFilter("fieldFilter", filter);
fProvider.setDefaultFilter(filter);
ObjectMapper mapper = new ObjectMapper();
mapper.setFilters(fProvider);
String json = mapper.writeValueAsString(o);
return json;
}
However, the filter is never applied. It always serializes all properties.
Set<String> fields = new HashSet<String>(); fields.add("name");
String json = Serializer.serializeOnlyGivenFields(e, fields);
System.out.println(json);
{"name":"Test entity","description":"Test description"}
I have also tried to register the FilterProvider on the ObjectWriter, but same result:
String json = mapper.writer(fProvider).writeValueAsString(o);
What am I missing? Is there a nice way to achieve this with Jackson?
Based on http://www.cowtowncoder.com/blog/archives/2011/09/entry_461.html an alternate way to set up the filter is setting up a class that extends JacksonAnnotationIntrospector and overrides findFilterId. You can then specify to find your filter in the findFilterId. This could be made to be as robust if you want based on some other map or algorithm. Below is sample code. Not sure if the performance is better than the solution above but it seems to be simpler and probably more easily extensible. I was doing this for serializing CSV using Jackson. Any feedback is welcome!
public class JSON {
private static String FILTER_NAME = "fieldFilter";
public static String serializeOnlyGivenFields(Object o,
Collection<String> fields) throws JsonProcessingException {
if ((fields == null) || fields.isEmpty()) fields = new HashSet<String>();
Set<String> properties = new HashSet<String>(fields);
SimpleBeanPropertyFilter filter =
new SimpleBeanPropertyFilter.FilterExceptFilter(properties);
SimpleFilterProvider fProvider = new SimpleFilterProvider();
fProvider.addFilter(FILTER_NAME, filter);
ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector( new AnnotationIntrospector() );
String json = mapper.writer(fProvider).writeValueAsString(o);
return json;
}
private static class AnnotationIntrospector extends JacksonAnnotationIntrospector {
#Override
public Object findFilterId(Annotated a) {
return FILTER_NAME;
}
}
}
One additional thing is that you have to indicate Java classes for which filter is to be used by #JsonFilter annotation:
#JsonFilter("fieldFilter")
public class MyType { }
and then it should apply.
I have found a solution based on Jackson: How to add custom property to the JSON without modifying the POJO. I override BeanSerializer#serializeFields to always use BeanSerializer#serializeFieldsFiltered instead. This way the filter is always applied.
Performance-wise not a very good solution, since an ObjectMapper has to be constructed at every method call. Feel free to post improvements or suggestions!
Module implementation:
public class FilteredModule extends SimpleModule {
private static final long serialVersionUID = 1L;
#Override
public void setupModule(SetupContext context) {
super.setupModule(context);
context.addBeanSerializerModifier(new BeanSerializerModifier() {
#Override
public JsonSerializer<?> modifySerializer(
SerializationConfig config,
BeanDescription beanDesc,
JsonSerializer<?> serializer) {
if (serializer instanceof BeanSerializerBase) {
return new FilteredBeanSerializer(
(BeanSerializerBase) serializer);
}
return serializer;
}
});
}
private class FilteredBeanSerializer extends BeanSerializer {
public FilteredBeanSerializer(BeanSerializerBase source) {
super(source);
}
#Override
protected void serializeFields(Object arg0, JsonGenerator arg1,
SerializerProvider arg2) throws IOException,
JsonGenerationException {
super.serializeFieldsFiltered(arg0, arg1, arg2);
}
}
}
API method:
public static String serializeOnlyGivenFields(Object o,
Collection<String> fields) throws JsonProcessingException {
if ((fields == null) || fields.isEmpty()) fields = new HashSet<String>();
Set<String> properties = new HashSet<String>(fields);
SimpleBeanPropertyFilter filter =
new SimpleBeanPropertyFilter.FilterExceptFilter(properties);
SimpleFilterProvider fProvider = new SimpleFilterProvider();
fProvider.addFilter("fieldFilter", filter);
fProvider.setDefaultFilter(filter);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new FilteredModule());
String json = mapper.writer(fProvider).writeValueAsString(o);
return json;
}
Example
Entity e = new Entity("Test entity", "Test description");
Set<String> fields = new HashSet<String>(); fields.add("name");
String json = JSON.serializeOnlyGivenFields(e, fields);
System.out.println(json);
{"name":"Test entity"}
Benchmark: 1000 iterations on the same object
serializeOnlyGivenFields: 536 ms
serialize (reuses ObjectMapper): 23 ms

Gson to json returning all the elements of subclass also

I have a situation where the gson is returning the child object elements also when i tried to make a json string from parent object. how to eliminate the same.
Here is the code i am having.
Class Image {
private int imageID;
private String imageName;
// Getters and setters
}
Class ImageDetails extends Image {
private String imageType;
private byte[] imageData;
//Getters and setters
}
Class Test {
// Setting the image Object, and the imageDetails.
// calling the gson for json string
String jsonString = GsonString.UserFeed(ImageObject)
// This jsonString has all the elements from the ImageDetails Object also which i do not want.
}
Class GsonString {
public static String UserFeed(Object feedData) {
String feeds = null;
Gson gson = new Gson();
feeds = gson.toJson(feedData);
return feeds;
}
}
You only have to specify the class you want to serialize, using toJson(Object src,Type typeOfSrc)
A simple example:
class Bob {
private String bobName = "Bob";
}
class Pete extends Bob {
private String peteName = "Bob";
}
public static void main(String[] args) {
Object o = new Pete();
System.out.println(new Gson().toJson(o));
System.out.println(new Gson().toJson(o, Bob.class));
}
Output:
{"peteName":"Bob","bobName":"Bob"}
{"bobName":"Bob"}

Unmarshalling JSON array via Jettison/Resteasy

Ran into a similar problem like the following forum post:
http://jersey.576304.n2.nabble.com/parsing-JSON-with-Arrays-using-Jettison-td5732207.html
Using Resteasy 2.0.1GA with Jettison 1.2 and getting a problem marshalling arrays when involving namespace mappings. See code below. Basically if the number of array entries are greater than one and namespace mappings are used. Anybody else run into this problem? The Nabble form poster got around it by writing a custom unmarshaller.
I either need to isolate the Jettison bug or write a Resteasy extension of the JettisonMappedUnmarshaller class (which hands over the namespace mappings and unmarshaller to the Jettison Configuration).
The following code doesn't unmarshall (post step) if the properties variables contains 2 or more entries.
public class Experimenting {
#Path("test")
public static class MyResource {
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Property", propOrder = { "name", "value" })
public static class MyProperty {
#XmlElement(name = "Name", required = true)
protected String name;
#XmlElement(name = "Value", required = true)
protected String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
#XmlType(name = "MyElement", propOrder = { "myProperty" })
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "MyElement", namespace = "http://www.klistret.com/cmdb/ci/commons")
#Mapped(namespaceMap = { #XmlNsMap(namespace = "http://www.klistret.com/cmdb/ci/commons", jsonName = "com.klistret.cmdb.ci.commons") })
public static class MyElement {
#XmlElement(name = "MyProperty", namespace = "http://www.klistret.com/cmdb/ci/commons")
protected List myProperty;
public List getMyProperty() {
if (myProperty == null) {
myProperty = new ArrayList();
}
return this.myProperty;
}
public void setMyProperty(List myProperty) {
this.myProperty = myProperty;
}
}
#GET
#Path("myElement/{id}")
#Produces(MediaType.APPLICATION_JSON)
public MyElement getMy(#PathParam("id")
Long id) {
MyElement myElement = new MyElement();
MyProperty example = new MyProperty();
example.setName("example");
example.setValue("of a property");
MyProperty another = new MyProperty();
another.setName("another");
another.setValue("just a test");
MyProperty[] properties = new MyProperty[] { example, another };
myElement.setMyProperty(Arrays.asList(properties));
return myElement;
}
#POST
#Path("/myElement")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public MyElement createMy(MyElement myElement) {
List properties = myElement.getMyProperty();
System.out.println("Properties size: " + properties.size());
return myElement;
}
}
private Dispatcher dispatcher;
#Before
public void setUp() throws Exception {
// embedded server
dispatcher = MockDispatcherFactory.createDispatcher();
dispatcher.getRegistry().addPerRequestResource(MyResource.class);
}
#Test
public void getAndCreate() throws URISyntaxException,
UnsupportedEncodingException {
MockHttpRequest getRequest = MockHttpRequest.get("/test/element/44");
MockHttpResponse getResponse = new MockHttpResponse();
dispatcher.invoke(getRequest, getResponse);
String getResponseBodyAsString = getResponse.getContentAsString();
System.out.println(String.format(
"Get Response code [%s] with payload [%s]", getResponse
.getStatus(), getResponse.getContentAsString()));
MockHttpRequest postRequest = MockHttpRequest.post("/test/element");
MockHttpResponse postResponse = new MockHttpResponse();
postRequest.contentType(MediaType.APPLICATION_JSON);
postRequest.content(getResponseBodyAsString.getBytes("UTF-8"));
dispatcher.invoke(postRequest, postResponse);
System.out.println(String.format(
"Post Response code [%s] with payload [%s]", postResponse
.getStatus(), postResponse.getContentAsString()));
}
}
Do you have to use Jettison? If not I would recommend just switching to use Jackson instead; this typically solves array/list related problems (problem with Jettison is that it converts to XML model, which makes it very hard to tell arrays from objects -- there are bugs, too, but it is fundamentally hard thing to get working correctly).