Parse JSON with Gson - java

How can I parse the JSON below with Gson?
Now I use:
private AttachChildDataModel parseSuccess(String content){
Gson gson = new Gson();
return gson.fromJson(content, AttachChildDataModel.class);
}
Where AttachChildDataModel has these member variables:
private Integer adultId;
private Integer childId;
private PlatformEnum platform;
private String regId;
private Date loginDate;
private Date logoutDate;
private ClientApp clientApp;
The Json string I'm trying to parse is:
{"log":
{
"childId":2,
"adultId":1,
"logoutDate":null,
"platform":"IPHONE",
"regId":null,
"loginDate":1325419200000,
"clientApp":"CHILD_APP"
}
}
When I put the object into the Spring ModelView, I add it under name log. The problematic thing is when I try to parse it with Gson. Right now, I manually remove the "log" prefix and the "}" postfix with String#substring, but I think there's a better solution.

To solve your problem, just create a "foo" class like this:
package stackoverflow.questions.q15614008;
public class Foo {
public AttachChildDataModel log;
}
and use it as base class for parsing in Gson:
package stackoverflow.questions.q15614008;
import com.google.gson.*;
public class Q15614008 {
public static void main(String[] arg) {
String testString = "{\"log\": "
+ " {"
+ "\"childId\":2," + "\"adultId\":1,"
+ "\"logoutDate\":null,"
+ "\"platform\":\"IPHONE\","
+ "\"regId\":null,"
+ "\"loginDate\":1325419200000,"
+ "\"clientApp\":\"CHILD_APP\"}"
+ "}";
Gson gson = new Gson();
Foo foo = gson.fromJson(
testString, Foo.class);
System.out.println("Result: " + foo.log.toString());
}
}
Then use only the log member variable of the Foo class.

Related

Deserialize inner object of deserialized object using gson

We are deserializing top-level RefundAttrs attribute while fetching transaction object from DB. I cannot change this part.
How can I deserialize RefundAttrs.metadata field into RefundEventData? I am getting com.google.gson.stream.MalformedJsonException: Unterminated object at line 1 column 52 path $.reason
class RefundAttrs {
private String instrumentTxnId;
private Object metadata;
public String instrumentTxnId() {
return instrumentTxnId;
}
public Object metadata() {
return metadata;
}
#Override
public String toString() {
return String.format("{ instrumentTxnId=%s, metadata=%s }", this.instrumentTxnId, this.metadata);
}
}
class RefundEventData {
private String orderId;
private Double refundAmount;
private String reason;
#Override
public String toString() {
return String.format("{ orderId=%s, refundAmount=%s, reason=%s }", this.orderId, this.refundAmount, this.reason);
}
}
public static void main(String[] args) throws IOException {
Gson gson = new Gson();
String json = "{\n" +
" \"instrumentTxnId\": \"refund-101\",\n" +
" \"metadata\": {\n" +
" \"orderId\": \"011-acf\",\n" +
" \"refundAmount\": 100.0,\n" +
" \"reason\": \"User left\"\n" +
" }\n" +
"}";
RefundAttrs attrs = gson.fromJson(json, RefundAttrs.class);
System.out.println(attrs.metadata());
// {orderId=011-acf, refundAmount=100.0, reason=User left}
RefundEventData eventData = gson.fromJson(attrs.metadata().toString(), RefundEventData.class);
}
attrs.metadata().toString() generates invalid JSON text.
Convert the metadata to JSON:
RefundEventData eventData = gson.fromJson(gson.toJson(attrs.metadata()), RefundEventData.class);

Skip a hierarchy when deserialising with Gson

Let's suppose we have a json like this one, that cannot be modified.
And we want do deserilise it using Gson.
{
"user": {
"some_ids": {
"useless_key": [
"22a074ff-91bf-4599-9a9e-374d3f01b6e0",
"66c8ce85-f162-4d92-a836-198a17764efa",
"d0519a9e-bfa2-446c-bb98-746136a3e513"
]
}
}
}
We want to deserialise it in a class User like this one:
public class User {
#SerializedName("some_ids")
List<String> someIds;
}
The question:
The simple solution would be to create a UselessKey wrapper class and put the someIds list in it.
But is there a way to tell Gson to skip the node useless_keyand directly deserialise the List inside someIds ?
Since you still have to mark a field supposed to be processed differently, Gson does not provide anything like that. However you can implement such behavior. The closest thing to your request is #JsonAdapter
Suppose you have
private static final String JSON = "{\n"
+ " \"user\": {\n"
+ " \"some_ids\": {\n"
+ " \"useless_key\": [\n"
+ " \"22a074ff-91bf-4599-9a9e-374d3f01b6e0\",\n"
+ " \"66c8ce85-f162-4d92-a836-198a17764efa\",\n"
+ " \"d0519a9e-bfa2-446c-bb98-746136a3e513\"\n"
+ " ]\n"
+ " }\n"
+ " }\n"
+ "}";
public static void main(final String... args) {
final Gson gson = new Gson();
final Response response = gson.fromJson(JSON, Response.class);
out.println(response.getUser().getSomeIds());
}
The DTO Response class is defined as the follows:
final class Response {
private Response() { }
#SerializedName("user")
private final User user = null;
User getUser() { return user; }
static final class User {
private User() { }
#SerializedName("some_ids")
#JsonAdapter(IdsTypeAdapter.class)
private final List<String> someIds = null;
List<String> getSomeIds() { return someIds; }
}
}
The type adapter specified in #JsonAdapter(IdsTypeAdapter.class) above can be implemented as follows:
final class IdsTypeAdapter
extends TypeAdapter<List<String>> {
private static final String USELESS_PROPERTY = "useless_key";
private IdsTypeAdapter() {
}
#Override
public void write(final JsonWriter writer, final List<String> value) {
throw new UnsupportedOperationException();
}
#Override
public List<String> read(final JsonReader reader)
throws IOException {
reader.beginObject();
if ( !reader.nextName().equals(USELESS_PROPERTY) ) {
throw new UnsupportedOperationException("Expected: " + USELESS_PROPERTY);
}
reader.beginArray();
final List<String> ids = new ArrayList<>();
while ( reader.peek() == STRING ) {
ids.add(reader.nextString());
}
reader.endArray();
reader.endObject();
return unmodifiableList(ids);
}
}
The type adapter above is pretty easy, and promotes stream reading in order to improve performance (type adapters are also required by the #JsonAdapter annotation). And the result:
[22a074ff-91bf-4599-9a9e-374d3f01b6e0, 66c8ce85-f162-4d92-a836-198a17764efa, d0519a9e-bfa2-446c-bb98-746136a3e513]
Another option is use of JSON deserializers (can be registered in GsonBuilder), but the latter have performance impacts since they require the whole JSON tree to be built before a deserialization process begins. Another issue with JSON deserializers is that Gson does not support custom annotations, so in order to mark "special" fields you still need to create a wrapper class like class StringIds extends ArrayList<String> that later would even require a deserialization context to deserialize a given JsonElement to List<String> and then remapped back to StringIds. That's expensive. I would go with type adapters.
Just don't create the variable and getter and setter in your Model class. It will not then parse the key which is not found.

Using Gson fromJson method not working

I have a json string that is represented as below:
"{"RequestId":255,
"RequestTime":"2016-04-08T17:00:40.327",
"Otp":"123456",
"AppName":"This is my app name",
"IsAwaitingResponse":false}"
I also have a class object below that I wish to create using the Gson library fromJson() method...
public class AccessRequest{
public int Id;
public String RequestTime;
public String Otp;
public String AppName;
public Boolean IsAwaitingResponse;
}
Now when I call the gson.fromJson method it does not error but my object is not getting set. This is the code I'm using...
Gson gson = new Gson();
AccessRequest ar;
ar = gson.fromJson(jsonString, AccessRequest.class);
It is instead setting my 'ar' variable to some strange gson object that clearly is wrong but no error is thrown.
{serializeNulls:falsefactories:[Factory[typeHierarchy=com.google.gson.JsonElement,adapter=com.google.gson.internal.bind.TypeAdapters$25#e8dad0a],
From what I can see, there is no error in my Json string ( although I am new to json ) , so I'm not really sure why this method wouldn't be working...
You probably printed the gson object instead of the ar object, as #Pillar noted in the comments.
I've tried your example and it works as expected. Also, the Id field is not being set because the name of the property does not match. You should use #SerializedName or change any of the names. This has also been noted in the comments.
This is the working example:
package net.sargue.gson;
import com.google.gson.Gson;
import org.intellij.lang.annotations.Language;
public class SO36553536 {
public static void main(String[] args) {
#Language("JSON")
String json = "{\n" +
" \"RequestId\": 255,\n" +
" \"RequestTime\": \"2016-04-08T17:00:40.327\",\n" +
" \"Otp\": \"123456\",\n" +
" \"AppName\": \"This is my app name\",\n" +
" \"IsAwaitingResponse\": false\n" +
"}";
Gson gson = new Gson();
AccessRequest ar;
ar = gson.fromJson(json, AccessRequest.class);
System.out.println("ar.getClass() = " + ar.getClass());
System.out.println("ar = " + ar);
}
public class AccessRequest {
public int Id;
public String RequestTime;
public String Otp;
public String AppName;
public Boolean IsAwaitingResponse;
#Override
public String toString() {
return "AccessRequest{" + "Id=" + Id +
", RequestTime='" + RequestTime + '\'' +
", Otp='" + Otp + '\'' +
", AppName='" + AppName + '\'' +
", IsAwaitingResponse=" + IsAwaitingResponse +
'}';
}
}
}
And this is the execution output:
ar.getClass() = class net.sargue.gson.SO36553536$AccessRequest
ar = AccessRequest{Id=0
, RequestTime='2016-04-08T17:00:40.327'
, Otp='123456'
, AppName='This is my app name'
, IsAwaitingResponse=false}
As just need to change RequestId from Id class AccessRequest.
you will get proper output.

How can i deserialize Json string where object's field's subclass is included in json string, using Java and Jackson library

I am very new to Java. I have some classes Site, Instances, CloudInstance. Class Site has an attribute instances and class CloudInstance inherits class Instance. They are as follows-
public class Site extends BaseEntity {
private String siteName;
List<Instance> instances = Lists.newArrayList();
}
public class Instance extends BaseEntity {
private String instanceId;
private String name;
}
public class CloudInstance extends Instance {
private String availabilityZone;
private String instanceType
}
I am deserializing json string as follows -
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
BaseEntity obj = null;
obj = (BaseEntity) mapper.readValue(jsonStr, Site.class);
It works fine if my jsonStr does not contain fields of class 'CloudInstance' and contains field instance with Instance class's fields.
Problem - Now i want to deserialize the jsonStr which includes 'CloudInstance' classe's fiels as well as the part of 'instances' field of class 'Site'. Ex jsonStr is as follows -
{
"id": null,
"siteName": "demo",
"instances": [
{
"instanceId": "i-8c2ee5fc",
"name": "some-node",
"availabilityZone": "some-zone",
"instanceType": "t1.micro"
}]
}
For the above jsonStr i get following error
error: Unrecognized field \"availabilityZone\" and error: Unrecognized field \"instanceType\"
With lots of if else and dirty code i can get the obj of Site including above fields. But i want to implement clean solution for this.
Is there any library which can do this? Any help id valuable. Please help..!!
Thanks in advance.
What you are trying to achieve is called polymorphic deserialization. Your example fails because Jackson needs to know what instance type should be constructed from JSON and placed to the list of instances. Please refer to this wiki page for detailed explanation.
I have modified you example to demonstrate how it could work. I've added the instance type information in the #type field in the JSON representation. Also I've made all the classes immutable using constructors annotated with the #JsonCreator annotation to create instances.
public class JacksonPolymorphism {
public static class BaseEntity {
private final String id;
protected BaseEntity(String id) {
this.id = id;
}
}
public static class Site extends BaseEntity {
private final String siteName;
private final List<Instance> instances;
#JsonCreator
public Site(#JsonProperty("id") String id,
#JsonProperty("siteName") String siteName,
#JsonProperty("instances") List<Instance> instances) {
super(id);
this.siteName = siteName;
this.instances = instances;
}
#Override
public String toString() {
return "Site{" +
"siteName='" + siteName + '\'' +
", instances=" + instances +
'}';
}
}
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "#type")
#JsonTypeName(value = "simple")
public static class Instance extends BaseEntity {
private final String name;
#JsonCreator
public Instance(#JsonProperty("instanceId") String id,
#JsonProperty("name") String name) {
super(id);
this.name = name;
}
#Override
public String toString() {
return "Instance{" +
"name='" + name + '\'' +
'}';
}
}
#JsonTypeName("cloud")
public static class CloudInstance extends Instance {
private final String availabilityZone;
private final String instanceType;
public CloudInstance(#JsonProperty("instanceId") String id,
#JsonProperty("name") String name,
#JsonProperty("availabilityZone") String availabilityZone,
#JsonProperty("instanceType") String instanceType) {
super(id, name);
this.availabilityZone = availabilityZone;
this.instanceType = instanceType;
}
#Override
public String toString() {
return "CloudInstance{" +
"availabilityZone='" + availabilityZone + '\'' +
", instanceType='" + instanceType + '\'' +
'}';
}
}
public static final String JSON = "{\n" +
" \"id\": null,\n" +
" \"siteName\": \"demo\",\n" +
" \"instances\": [\n" +
" {\n" +
" \"#type\": \"cloud\",\n" +
" \"instanceId\": \"i-8c2ee5fc\",\n" +
" \"name\": \"some-node\",\n" +
" \"availabilityZone\": \"some-zone\",\n" +
" \"instanceType\": \"t1.micro\" \n" +
" }," +
" {\n" +
" \"#type\": \"simple\",\n" +
" \"instanceId\": \"ABC\",\n" +
" \"name\": \"FGF\"\n" +
" }]" +
" }";
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.registerSubtypes(CloudInstance.class);
System.out.println(mapper.readValue(JSON, Site.class));
}
}
Output:
Site{siteName='demo', instances=[CloudInstance{availabilityZone='some-zone', instanceType='t1.micro'}, Instance{name='FGF'}]}
I always had problems to deserialize JSON that contains List<...> objects with Jackson, so try to deserialize with Gson:
https://code.google.com/p/google-gson/
Take a look at the documentation in the methods fromJson and toJson.
I hope that can help,
Best regards
Never had consistent luck with Jackson or Gson, try Flex JSON instead:
JSONSerializer ser = new JSONSerializer();
String json = ser.deepSerialize(yourObject);
JSONDeserializer<YourMainType> der = new JSONDeserializer<YourMainType>();
YourMainType mainType = der.deserialize(json);
For this to work, all classes subject to serialization/deserialization must expose getters/setters consistent with Java Beans convention.
You can used GSON Library for de-serialize your Json into your class object.
There is function gson.fromJson(JSON String) which convert json string to class object.
Here is Sample code :
Gson json = new Gson();
Site site = json.fromJson(jsonStr, Site.class);
But in your code you have replace
List<Instance> instances = Lists.newArrayList();
this line in class Site with
List<CloudInstance> instances = new ArrayList<CloudInstance>();
Because your CloudInstance class extend Instance it means CloudInstance class include member of Instance class. As per json you need to do this to cast directly into class object.
May this will help you.

GSON: Cannot correctly parse JSON object

I am trying to parse a JSON object like the following with GSON:
{
"key1":"someValue",
"key2":{
"anotherKey1":"212586425",
"anotherKey2":"Martin"
}
}
This is the code:
Data data = new Gson().fromJson(json, Data.class);
Here is the Data class:
public class Data {
public String key1;
public Map key2; //This will break everything.
}
What I expect (I am new to GSON) is that it produces the value of key2 as a Map object.
However, I get an error Expected BEGIN_OBJECT but was STRING which makes me think that I am passing a String, where I should be passing a JSON object.
Isn't GSON parsing the whole JSON string I pass in the beginning? So eventually, I would like the new data source to be a Map Object. Is that feasible ?
Let Gson do the work. I defined Data as
package stackoverflow.questions.q19228349;
public class Data {
#Override
public String toString() {
return "Data [key1=" + key1 + ", key2=" + key2 + "]";
}
public String key1;
public Object key2;
}
and then I can parse both cases for key2:
package stackoverflow.questions.q19228349;
import com.google.gson.Gson;
public class Q19228349 {
public static void main(String[] args){
String json =
"{\"key1\":\"someValue\","+
"\"key2\":{ "+
" \"anotherKey1\":\"212586425\","+
" \"anotherKey2\":\"Martin\""+
" }"+
" }";
String json2 =
"{\"key1\":\"someValue\","+
"\"key2\":\"aString\""+
" }";
Gson g = new Gson();
Data d = g.fromJson(json, Data.class);
System.out.println("First: " +d);
Data d2 = g.fromJson(json2, Data.class);
System.out.println("Second: "+d2);
}
}
This is the result:
First: Data [key1=someValue, key2={anotherKey1=212586425,
anotherKey2=Martin}] Second: Data [key1=someValue, key2=aString]

Categories

Resources