I have a json string:
{
"id":123,
"name":"",
"details":{}
}
I want to parse to this object:
class Student {
int id;
String name;
String details;
}
This is the error that I get:
java.lang.RuntimeException: Unable to start activity ComponentInfo{xxx.xxx/xxx.xxx.MainActivity}: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected STRING but was BEGIN_OBJECT
The reason I want details as string not as a JsonObject because I'm using Realm DB object and persist that to the database. How can I tell Gson I want the details as string.
The details value, {}, is an object not a string. It will not be interpreted as a string unless you quote it like this:
{
"id":123,
"name":"",
"details":"{}"
}
GSON is telling you "Expected STRING but was BEGIN_OBJECT". This makes sense, because you're giving it a type signature with a String attribute named details, but your serialization has an attribute named details that contains an empty object.
I didn't find a nice way to solve it but currently I copy the json object and stringify it to variable, remove it from the json element and then call gson fromJson.
Related
I am getting String jsonObject in my controller.
The structure is following:
{
"name":"name",
"schema": {
...
...
}
}
I need to parse it into a Plain Old Java Object and receive schema as a String (saving the structure). When I am using System.out.print("schema"), I expect to see:
{
...
...
}
I have a POJO Collection with String name and Object schema fields.
I am using GSON to get Collection.class from String json:
new Gson().fromJson(json, Collection.class);
When I try to print Collection.schema I get the following output:
{......} - in a one row.
I really need to get this object as a String without formatting
This should work. Basically, you hydrate your Collection object and then just send your schema back through Gson. Done.
Gson gson = new Gson();
Collection collection = gson.fromJson(json, Collection.class);
String schema = gson.toJson(collection.getSchema());
System.out.println(schema);
Question could be asked as to why you are accepting a String from your controller? Perhaps you can get the framework you are using to pass in a fully converted Collection object and save yourself a step?
Also, for your Collection.schema, it's common to use Map<String, Object> instead of Object for this type of "schema"-less type of paradigm.
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}}";
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.
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.