Deserialize with gson and null values - android

I am trying to deserialize my own class with a null value. But my code doesn't work.
My json:
{"Text":null,"Code":0,"Title":"This is Sparta!"}
In my method I do the following:
this.setText(gson.fromJson(jsonObject.getString("Text"), String.class));
this.setTitle(gson.fromJson(jsonObject.getString("Title"), String.class));
this.setCode(gson.fromJson(jsonObject.getString("Faccode"), Integer.class))
I am not deserialize the whole object, because there can be a List<T>, too.
The error:
myapp W/System.err? com.google.gson.JsonSyntaxException: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 6 path $
myapp W/System.err? at com.google.gson.Gson.assertFullConsumption(Gson.java:786)
myapp W/System.err? at com.google.gson.Gson.fromJson(Gson.java:776)
myapp W/System.err? at com.google.gson.Gson.fromJson(Gson.java:724)
myapp W/System.err? at com.google.gson.Gson.fromJson(Gson.java:696)

First, you must read about how to parse using gson. You can find some example here.
Now you know how to parse, you can still have problem with null values. To solve it you must tell gson to (de)serialize null using
Gson gson = new GsonBuilder().serializeNulls().create();
From the serializeNulls() doc
Configure Gson to serialize null fields. By default, Gson omits all fields that are null during serialization.
EDIT (Not tested, based on doc)
In order to get some distinct value you can do
String json = ""; //Your json has a String
JsonObject jsonObject = new JsonParser().parse(json).getAsJsonObject();
//If null, use a default value
JsonElement nullableText = jsonObject.get("Text");
String text = (nullableText instanceof JsonNull) ? "" : nullableText.getAsString();
String title = jsonObject.get("Title").toString();
int code = jsonObject.get("Code").getAsInt();
Otherwise if you have this pojo
public class MyElement {
#SerializedName("Text")
private String text;
#SerializedName("Title")
private String title;
#SerializedName("Code")
private int code;
}
you can parse using
String json = ""; //Your json has a String
Gson gson = new GsonBuilder().serializeNulls().create();
MyElement myElement = gson.fromJson(json, MyElement.class);

I had a similar problem (an exception thrown on null value) with the following POJO:
public class MyElement {
private String something;
private String somethingElse;
private JsonObject subEntry; // this doesn't allow deserialization of `null`!
}
and this code:
parsedJson = gson.fromJson(json, MyElement.class)
when subEntry returned by the backend was null.
I fixed it by changing the type of subEntry from JsonObject to JsonElement which is a parent class of both JsonObject and JsonNull, to allow deserialization of null values.
public class MyElement {
private String something;
private String somethingElse;
private JsonElement subEntry; // this allows deserialization of `null`
}
To later check for null at runtime, you'd do as follows:
if (parsedJson.subEntry instanceof JsonNull) {
...
} else {
...
}

Related

Android kotlin object to json values order [duplicate]

Seems like Gson.toJson(Object object) generates JSON code with randomly spread fields of the object. Is there way to fix fields order somehow?
public class Foo {
public String bar;
public String baz;
public Foo( String bar, String baz ) {
this.bar = bar;
this.baz = baz;
}
}
Gson gson = new Gson();
String jsonRequest = gson.toJson(new Foo("bar","baz"));
The string jsonRequest can be:
{ "bar":"bar", "baz":"baz" } (correct)
{ "baz":"baz", "bar":"bar" } (wrong sequence)
You'd need to create a custom JSON serializer.
E.g.
public class FooJsonSerializer implements JsonSerializer<Foo> {
#Override
public JsonElement serialize(Foo foo, Type type, JsonSerializationContext context) {
JsonObject object = new JsonObject();
object.add("bar", context.serialize(foo.getBar());
object.add("baz", context.serialize(foo.getBaz());
// ...
return object;
}
}
and use it as follows:
Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class, new FooJsonSerializer()).create();
String json = gson.toJson(foo);
// ...
This maintains the order as you've specified in the serializer.
See also:
Gson User Guide - Custom serializers and deserializers
If GSON doesn't support definition of field order, there are other libraries that do. Jackson allows definining this with #JsonPropertyOrder, for example. Having to specify one's own custom serializer seems like awful lot of work to me.
And yes, I agree in that as per JSON specification, application should not expect specific ordering of fields.
Actually Gson.toJson(Object object) doesn't generate fields in random order. The order of resulted json depends on literal sequence of the fields' names.
I had the same problem and it was solved by literal order of properties' names in the class.
The example in the question will always return the following jsonRequest:
{ "bar":"bar", "baz":"baz" }
In order to have a specific order you should modify fields' names, ex: if you want baz to be first in order then comes bar:
public class Foo {
public String f1_baz;
public String f2_bar;
public Foo ( String f1_baz, String f2_bar ) {
this.f1_baz = f1_baz;
this.f2_bar = f2_bar;
}
}
jsonRequest will be { "f1_baz ":"baz", "f2_bar":"bar" }
Here's my solution for looping over json text files in a given directory and writing over the top of them with sorted versions:
private void standardizeFormat(File dir) throws IOException {
File[] directoryListing = dir.listFiles();
if (directoryListing != null) {
for (File child : directoryListing) {
String path = child.getPath();
JsonReader jsonReader = new JsonReader(new FileReader(path));
Gson gson = new GsonBuilder().setPrettyPrinting().registerTypeAdapter(LinkedTreeMap.class, new SortedJsonSerializer()).create();
Object data = gson.fromJson(jsonReader, Object.class);
JsonWriter jsonWriter = new JsonWriter(new FileWriter(path));
jsonWriter.setIndent(" ");
gson.toJson(data, Object.class, jsonWriter);
jsonWriter.close();
}
}
}
private class SortedJsonSerializer implements JsonSerializer<LinkedTreeMap> {
#Override
public JsonElement serialize(LinkedTreeMap foo, Type type, JsonSerializationContext context) {
JsonObject object = new JsonObject();
TreeSet sorted = Sets.newTreeSet(foo.keySet());
for (Object key : sorted) {
object.add((String) key, context.serialize(foo.get(key)));
}
return object;
}
}
It's pretty hacky because it depends on the fact that Gson uses LinkedTreeMap when the Type is simply Object. This is an implementation details that is probably not guaranteed. Anyway, it's good enough for my short-lived purposes...

GSON sorting keys automatically in toJson() [duplicate]

Seems like Gson.toJson(Object object) generates JSON code with randomly spread fields of the object. Is there way to fix fields order somehow?
public class Foo {
public String bar;
public String baz;
public Foo( String bar, String baz ) {
this.bar = bar;
this.baz = baz;
}
}
Gson gson = new Gson();
String jsonRequest = gson.toJson(new Foo("bar","baz"));
The string jsonRequest can be:
{ "bar":"bar", "baz":"baz" } (correct)
{ "baz":"baz", "bar":"bar" } (wrong sequence)
You'd need to create a custom JSON serializer.
E.g.
public class FooJsonSerializer implements JsonSerializer<Foo> {
#Override
public JsonElement serialize(Foo foo, Type type, JsonSerializationContext context) {
JsonObject object = new JsonObject();
object.add("bar", context.serialize(foo.getBar());
object.add("baz", context.serialize(foo.getBaz());
// ...
return object;
}
}
and use it as follows:
Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class, new FooJsonSerializer()).create();
String json = gson.toJson(foo);
// ...
This maintains the order as you've specified in the serializer.
See also:
Gson User Guide - Custom serializers and deserializers
If GSON doesn't support definition of field order, there are other libraries that do. Jackson allows definining this with #JsonPropertyOrder, for example. Having to specify one's own custom serializer seems like awful lot of work to me.
And yes, I agree in that as per JSON specification, application should not expect specific ordering of fields.
Actually Gson.toJson(Object object) doesn't generate fields in random order. The order of resulted json depends on literal sequence of the fields' names.
I had the same problem and it was solved by literal order of properties' names in the class.
The example in the question will always return the following jsonRequest:
{ "bar":"bar", "baz":"baz" }
In order to have a specific order you should modify fields' names, ex: if you want baz to be first in order then comes bar:
public class Foo {
public String f1_baz;
public String f2_bar;
public Foo ( String f1_baz, String f2_bar ) {
this.f1_baz = f1_baz;
this.f2_bar = f2_bar;
}
}
jsonRequest will be { "f1_baz ":"baz", "f2_bar":"bar" }
Here's my solution for looping over json text files in a given directory and writing over the top of them with sorted versions:
private void standardizeFormat(File dir) throws IOException {
File[] directoryListing = dir.listFiles();
if (directoryListing != null) {
for (File child : directoryListing) {
String path = child.getPath();
JsonReader jsonReader = new JsonReader(new FileReader(path));
Gson gson = new GsonBuilder().setPrettyPrinting().registerTypeAdapter(LinkedTreeMap.class, new SortedJsonSerializer()).create();
Object data = gson.fromJson(jsonReader, Object.class);
JsonWriter jsonWriter = new JsonWriter(new FileWriter(path));
jsonWriter.setIndent(" ");
gson.toJson(data, Object.class, jsonWriter);
jsonWriter.close();
}
}
}
private class SortedJsonSerializer implements JsonSerializer<LinkedTreeMap> {
#Override
public JsonElement serialize(LinkedTreeMap foo, Type type, JsonSerializationContext context) {
JsonObject object = new JsonObject();
TreeSet sorted = Sets.newTreeSet(foo.keySet());
for (Object key : sorted) {
object.add((String) key, context.serialize(foo.get(key)));
}
return object;
}
}
It's pretty hacky because it depends on the fact that Gson uses LinkedTreeMap when the Type is simply Object. This is an implementation details that is probably not guaranteed. Anyway, it's good enough for my short-lived purposes...

Custom GSON parser exclude object instances based on property value

When parsing JSON in Android using the GSON parser, I'd like to implement a rule that will exclude any objects from being created based on property value. For example:
{"people": [
{"first_name": "Bob"},
{"first_name": "Bob", "last_name": "Loblaw"}]}
I want to exclude the first person object because it doesn't have a last name property.
Is this possible at parse time?
It is possible with JsonDeserializer.
Suppose you would have POJOs like
public class Response {
#Getter
private List<Person> people = new ArrayList<>();
}
and
public class Person {
#Getter #Setter
private String first_name, last_name;
}
Creating JsonDeserializer like
public class PersonResponseDeserializer implements JsonDeserializer<Response> {
// Create a new gson to make the default parsing for response object
private final Gson gson = new Gson();
#Override
public Response deserialize(JsonElement json, Type typeOfT
, JsonDeserializationContext context) throws JsonParseException {
Response r = gson.fromJson(json, typeOfT);
// Remove all persons from R that have last name null
r.getPeople().removeAll(
r.getPeople().stream().filter( p -> p.getLast_name() == null )
.collect(Collectors.toSet())
);
return r;
}
}
could then be used like
Gson gson = new GsonBuilder()
.registerTypeAdapter(Response.class, new PersonResponseDeserializer())
.create();
Response r = gson.fromJson(s, Response.class);
So this is if it is required to be done at the parse time. Maybe it is otherwise better to loop the People after parsing and exclude Persons without last name then.

MappingFacebook JSON response to POJO

I am making an API call to Facebook and receiving the following Json object:
{"first_name":"FirstName",
"last_name":"LastName",
"email":"email#email.com",
"picture":{"data":{"is_silhouette":true,"url":"pictureUrl"}},"id":"12345"}
Instead of deserializing the object manually, I am currently using Gson for it, like this:
FacebookProfileModel facebookProfileModel = new Gson().fromJson(object.toString(), FacebookProfileModel.class);
Here's how my POJO looks like:
#SerializedName("first_name")
String mFirstName;
#SerializedName("last_name")
String mLastName;
#SerializedName("email")
String mEmail;
#SerializedName("url")
String mUrl;
Obviously, I am receiving all the values except for the url, since the value is in 2 Json objects: picture and data. I guess one possible solution but not the best would be to create the Picture object within the Facebook Model and then the Data object within the Picture object but feels bad creating 2 more pojos for a String. Any other solutions?
There is no annotation based solution for this. However, the custom de-serializer would resolve this problem.
Custom Deserializer:-
public class FacebookProfileModelDeserializer implements JsonDeserializer<FacebookProfileModel> {
#Override
public FacebookProfileModel deserialize(JsonElement paramJsonElement, Type paramType,
JsonDeserializationContext paramJsonDeserializationContext) throws JsonParseException {
String url = paramJsonElement.getAsJsonObject().get("picture").getAsJsonObject().get("data").getAsJsonObject()
.get("url").getAsString();
FacebookProfileModel facebookProfileModel = new Gson().fromJson(paramJsonElement.getAsJsonObject(),
FacebookProfileModel.class);
facebookProfileModel.setmUrl(url);
return facebookProfileModel;
}
}
Main method:-
public static void main(String[] args) {
String jsonString = "{\"first_name\":\"FirstName\",\"last_name\":\"LastName\",\"email\":\"email#email.com\",\"picture\":{\"data\":{\"is_silhouette\":true,\"url\":\"pictureUrl\"}},\"id\":\"12345\"}";
Gson gson = new GsonBuilder()
.registerTypeAdapter(FacebookProfileModel.class, new FacebookProfileModelDeserializer())
.create();
FacebookProfileModel faceBookProfileModel = gson.fromJson(jsonString, FacebookProfileModel.class);
System.out.println(faceBookProfileModel.toString());
}

Using GSON to deserialize nested array

Giving
JSON
// imagine this is JSON of a city
{
"title" : "Troy"
"people" : [
{
{
"title" : "Hector",
"status" : "Dead"
},
{
"title" : "Paris",
"status" : "Run Away"
}
},
...
],
"location" : "Mediteranian",
"era" : "Ancient",
...
}
City
public class City {
#SerializeName("title")
String title;
#SerializeName("people")
List<Person> people;
#SerializeName("location")
String location;
#SerializeName("era")
String era;
...
}
Person
public class Person {
#SerializeName("title")
private String title;
#SerializeName("status")
private String status;
}
If having string of JSON above, it is possible to create list of person
A. without having to deserialize City first like following
City city = new Gson().fromJson(json, City.class)
ArrayList<Person> people = city.people;
And
B. without having to convert string to JSONObject, get JSONArray and then convert back to string like following
String peopleJsonString = json.optJSONArray("people").toString
ArrayList<Person> people = new Gson().fromJSON(peopleJsonString, Person.class);
You can use a custom JsonDeserializer, which is part of Gson (com.google.gson.JsonDeserializer).
Simple example:
public class WhateverDeserializer implements JsonDeserializer<Whatever> {
#Override
public Whatever deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
Whatever whatever = new Whatever();
// Fetch the needed object here
// whatever.setNeededObject(neededObject);
return whatever;
}
}
You can then apply this deserializer like this:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Whatever.class, new WhateverDeserializer())
.create();
There is a full example of how to use a custom deserializer, including a super detailed explanation, on this page: http://www.javacreed.com/gson-deserialiser-example/
I don't think you can get the list directly without parsing the json array. You need to parse the array. And it would be faster via Gson;
If you strictly need (only array) and you won't be using any other json object . Simply delete them, so that gson won't parse them.

Categories

Resources