Right now I am using Gson to deserialize JSON to Object.
The JSON looks like this:
[
{
"hash":"c8b2ce0aacede58da5d2b82225efb3b7",
"instanceid":"aa49882f-4534-4add-998c-09af078595d1",
"text":"{\"C_FirstName\":\"\",\"ContactID\":\"2776967\",\"C_LastName\":\"\"}",
"queueDate":"2016-06-28T01:03:36"
}
]
And my entity object looks like this:
public class AppCldFrmContact {
public String hash;
public String instanceid;
public HashMap<String,String> text;
public String queueDate;
}
If text was a String data type, everything would be fine. But then I wouldn't be able to access different fields as I want to.
Is there a way to convert given JSON to Object I want?
The error I am getting is: Expected BEGIN_OBJECT but was STRING at line 1 column 174, which is understandable if it cannot parse it.
The code doing the parsing:
Type listType = new TypeToken<List<AppCldFrmContact>>() {
}.getType();
List<AppCldFrmContact> contacts = gson.fromJson(response.body, listType);
For you expected result, JSON data should be like below format,
[
{
"hash":"c8b2ce0aacede58da5d2b82225efb3b7",
"instanceid":"aa49882f-4534-4add-998c-09af078595d1",
"text":{"C_FirstName":"","ContactID":"2776967","C_LastName":""},
"queueDate":"2016-06-28T01:03:36"
}
]
You are getting this error because text field is a JSON map serialized to the string. If it is an actual your data and not a just an example, you can annotate a field with #JsonDeserialize and write your own custom JsonDeserializer<HashMap<String,String>> which will make deserialization 2 times.
Related
So I have this variable specCifDetailsReturn which contains the ff. payload
[
{"ax21:cHType":"S",
"ax21:cardNumber":4***********7126,"ax21:returnCde":"00",
"ax21:cancelCode":"",
"ax21:vipCode":"",
"ax21:custrNbr":"0*****3426"},
{"ax21:cHType":"S",
"ax21:cardNumber":4***********3038,"ax21:returnCde":"00",
"ax21:cancelCode":"H",
"ax21:vipCode":"",
"ax21:custrNbr":"0*****3426"}
]
And the ff. Model Class to extract the params I need from the Array
#Data
#AllArgsConstructor
#NoArgsConstructor
#JsonNaming(PropertyNamingStrategy.UpperCamelCaseStrategy.class)
public final class SpecCifInfo {
#JsonAlias("ax21:cHType")
private String cHType;
#JsonAlias("ax21:cardNumber")
private String cardNumber;
}
I am trying to convert it to a Java ArrayList so that I could loop into it and find a card number. But for some reason it always throws a null value on the log even though the specCifDetailsReturn variable has a value. Below is the snippet of my code.
Gson gson = new Gson();
Type type = new TypeToken<List<SpecCifInfo>>(){}.getType();
ArrayList<SpecCifInfo> specDetails = gson.fromJson(specCifDetailsReturn.toString(),type);
for (SpecCifInfo specInfo : specDetails){
LOGGER.debug("Spec CIF Details", specInfo.getCHType() + "-" + specInfo.getCardNumber());
}
Sample Output of the SpecCifInfo Object that has null values
Those annotations are for the Jackson library, and you are manually using Gson. You should either keep them and just let Spring handle the deserialization for you by specifying a List<SpecCifInfo> parameter in the controller method, or you should use GSON's #SerializedName annotation. Either way will work.
public static String toJsonString(Object obj) {
Gson gson = new Gson();
return gson.toJson(obj);
}
Using this method to json-lize the object and then use this to de-json-lize
Gson gson = new Gson();
gson.fromJson(this.getThreadDumpVoJson(), ThreadDumpVo.class);
Everything works fine until I just added a new field to this ThreadDumpVo
Map<StackStatePair, Integer> traceStatePairSortedSizeGroup;
then Exception thrown as follows:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1540 path $.traceStatePairSortedSizeGroup.
The StackStatePair is defined as follows:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class StackStatePair {
String callStack;
String state;
}
Previously I was trying to use import javafx.util.Pair; but it prompts the same issue, what's going on here?
I cannot use composite key?
"traceStatePairSortedSizeGroup": {
"StackStatePair(callStack\u003dDeadLoopThread.lambda$createBusyThread$0(DeadLoopThread.java:7)|DeadLoopThread$$Lambda$1/2080166188.run(Unknown Source)|java.lang.Thread.run(Thread.java:748), state\u003dRUNNABLE)": 1,
"StackStatePair(callStack\u003djava.lang.Object.wait(Native Method)|java.lang.Object.wait(Object.java:502)|java.lang.ref.Reference.tryHandlePending(Reference.java:191)|java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153), state\u003dWAITING)": 1,
"StackStatePair(callStack\u003djava.lang.Object.wait(Native Method)|java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)|java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)|java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209), state\u003dWAITING)": 1
}
No, you cannot use such composite key if you are parsing the JSON using Gson. The key itself is not a JSON String, which makes it not parse-able using Gson. It can only be parsed as a String.
This is not an array inside the traceStatePairSortedSizeGroup. If it was an array, then we could have stored the strings into a String array and then we could have parsed the values from the array. That's why you have the JsonSyntaxException.
If there is any chance to get your JSON body modified, then I would like to suggest you change it to keep it simple.
(This post is meant to be a canonical question with a sample answer provided below.)
I'm trying to deserialize some JSON content into a custom POJO type with Gson#fromJson(String, Class).
This piece of code
import com.google.gson.Gson;
public class Sample {
public static void main(String[] args) {
String json = "{\"nestedPojo\":[{\"name\":null, \"value\":42}]}";
Gson gson = new Gson();
gson.fromJson(json, Pojo.class);
}
}
class Pojo {
NestedPojo nestedPojo;
}
class NestedPojo {
String name;
int value;
}
throws the follow exception
Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 16 path $.nestedPojo
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:200)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:103)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:196)
at com.google.gson.Gson.fromJson(Gson.java:810)
at com.google.gson.Gson.fromJson(Gson.java:775)
at com.google.gson.Gson.fromJson(Gson.java:724)
at com.google.gson.Gson.fromJson(Gson.java:696)
at com.example.Sample.main(Sample.java:23)
Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 16 path $.nestedPojo
at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:387)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:189)
... 7 more
Why can't Gson properly convert my JSON text to my POJO type?
As the exception message states
Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 16 path $.nestedPojo
while deserializing, Gson was expecting a JSON object, but found a JSON array. Since it couldn't convert from one to the other, it threw this exception.
The JSON format is described here. In short, it defines the following types: objects, arrays, strings, numbers, null, and the boolean values true and false.
In Gson (and most JSON parsers), the following mappings exist: a JSON string maps to a Java String; a JSON number maps to a Java Number type; a JSON array maps to a Collection type or an array type; a JSON object maps to a Java Map type or, typically, a custom POJO type (not mentioned previously); null maps to Java's null, and the boolean values map to Java's true and false.
Gson iterates through the JSON content that you provide and tries to deserialize it to the corresponding type you've requested. If the content doesn't match or can't be converted to the expected type, it'll throw a corresponding exception.
In your case, you provided the following JSON
{
"nestedPojo": [
{
"name": null,
"value": 42
}
]
}
At the root, this is a JSON object which contains a member named nestedPojo which is a JSON array. That JSON array contains a single element, another JSON object with two members. Considering the mappings defined earlier, you'd expect this JSON to map to a Java object which has a field named nestedPojo of some Collection or array type, where that types defines two fields named name and value, respectively.
However, you've defined your Pojo type as having a field
NestedPojo nestedPojo;
that is neither an array type, nor a Collection type. Gson can't deserialize the corresponding JSON for this field.
Instead, you have 3 options:
Change your JSON to match the expected type
{
"nestedPojo": {
"name": null,
"value": 42
}
}
Change your Pojo type to expect a Collection or array type
List<NestedPojo> nestedPojo; // consider changing the name and using #SerializedName
NestedPojo[] nestedPojo;
Write and register a custom deserializer for NestedPojo with your own parsing rules. For example
class Custom implements JsonDeserializer<NestedPojo> {
#Override
public NestedPojo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
NestedPojo nestedPojo = new NestedPojo();
JsonArray jsonArray = json.getAsJsonArray();
if (jsonArray.size() != 1) {
throw new IllegalStateException("unexpected json");
}
JsonObject jsonObject = jsonArray.get(0).getAsJsonObject(); // get only element
JsonElement jsonElement = jsonObject.get("name");
if (!jsonElement.isJsonNull()) {
nestedPojo.name = jsonElement.getAsString();
}
nestedPojo.value = jsonObject.get("value").getAsInt();
return nestedPojo;
}
}
Gson gson = new GsonBuilder().registerTypeAdapter(NestedPojo.class, new Custom()).create();
class Pojo {
NestedPojo nestedPojo;
}
in your json you have an array of nestedPojo
so either you change the code
NestedPojo[] nestedPojo;
or you change the json string
String json = "{\"nestedPojo\":{\"name\":null, \"value\":42}}";
I have an InputStreamReader reader that contains this JSON file:
http://barcelonaapi.marcpous.com/bus/nearstation/latlon/41.3985182/2.1917991/1.json
Also, I have a class Station that contains ID, streetName, city, utmX, utmy, lat, lon as members.
What should i do, if I want parse the JSON file with GSon, to return an List<Station>?
I tried this :
gson.fromJson(reader, new TypeToken<List<Station>>(){}.getType());
But it raised an IllegalStateException (Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2).
How to extract only data which interests me (members of my Station class)?
Is it possible with GSon, or I need to use the JSON standard API provided by Android SDK (with JSONObject and JSONArray)?
You're close, but in this case you can't directly map to a List<Station> because it is wrapped in json object (2 layers deep) that also contains some other fields. That's basically also what the error is trying to tell you: you're instructing Gson to map to an array/list of items (in json: [...]), but instead it encountered an object (in json: {...}).
The quickest solution is to create a POJO that reflects this json response. For example:
public class Response {
#SerializedName("code") public int mCode;
#SerializedName("data") public ResponseData mData;
}
public class ResponseData {
#SerializedName("transport") public String mTransport;
#SerializedName("nearstations") public List<Station> mStations;
}
Now you can map the json to the above Response and get the List<Station> from the result:
Response response = gson.fromJson(reader, Response.class);
List<Station> stations = response.mData.mStations;
// do something with stations...
If you like to do something a little more advanced, you can take a look into writing a custom deserialiser or type adapter.
Given the following JSON object
{
"id": 5,
"data: { ... }
}
Is it possible to map this to the following POJO?
class MyEntity {
int id;
Map<String, Object> data;
}
Because I would like to leave the data object open ended. Is this even possible or what is a better approach to go about this? I am doing this on Android.
I don't have any idea about Android application but you can achieve it using Gson library easily.
The JSON that is used in your post is not valid. It might be a typo. Please validate it here on JSONLint - The JSON Validator
Simply use Gson#fromJson(String, Class) method to convert a JSON string into the object of passed class type.
Remember the name of instance member must be exactly same (case-sensitive) as defined in JSON string as well. Read more about JSON Field Naming
Use GsonBuilder#setPrettyPrinting() that configures Gson to output Json that fits in a page for pretty printing.
Sample code:
String json = "{\"id\": 5,\"data\": {}}";
MyEntity myEntity = new Gson().fromJson(json, MyEntity.class);
String prettyJsonString = new GsonBuilder().setPrettyPrinting().create().toJson(myEntity);
System.out.println(prettyJsonString);
output:
{
"id": 5,
"data": {}
}