Gson, JSON and the subtleties of LinkedTreeMap
Forget about Java. You need to first understand the JSON format.
This is basically it
object
{}
{ members }
members
pair
pair , members
pair
string : value
array
[]
[ elements ]
elements
value
value , elements
value
string
number
object
array
true
false
null
Your second JSON String
(which has a missing "
) is the following (use jsonlint.com to format)
{
"created_at": "Sat Feb 08 15:37:37 +0000 2014",
"id": "432176397474623489",
"user": {
"id_str": "366301747",
"name": "somethingClever",
"screen_name": "somethingCoolAndClever"
}
}
The JSON is an object, outer {}
, that contains three pairs, created_at
which is a JSON string, id
which is also a JSON string, and user
which is a JSON object. That JSON object contains three more pairs which are all JSON strings.
You asked
How could I assign the values specified within these inner-brackets to variables?
Most advanced JSON parsing/generating libraries are meant to convert JSON to Pojos and back.
So you could map your JSON format to Java classes.
class Pojo {
@SerializedName("created_at")
private String createdAt;
private String id;
private User user;
}
class User {
@SerializedName("id_str")
private String idStr;
private String name;
@SerializedName("screen_name")
private String screenName;
}
// with appropriate getters, setters, and a toString() method
Note the @SerializedName
so that you can keep using Java naming conventions for your fields.
You can now deserialize your JSON
Gson gson = new Gson();
Pojo pojo = gson.fromJson(jsonInput2, Pojo.class);
System.out.println(pojo);
would print
Pojo [createdAt=Sat Feb 08 15:37:37 +0000 2014, id=432176397474623489", user=User [idStr=366301747, name=somethingClever, screenName=somethingCoolAndClever]]
showing that all the fields were set correctly.
Can anyone explain to me, or show me in code, how Gson handles stuff like this, and how I can use it?
The source code of Gson is freely available. You can find it online. It is complex and a source code explanation wouldn't fit here. Simply put, it uses the Class
object you provide to determine how it will map the JSON pairs. It looks at the corresponding class's fields. If those fields are other classes, then it recurs until it has constructed a map of everything it needs to deserialize.
In short, why does...print out null?
Because your root JSON object, doesn't have a pair with name name
. Instead of using Map
, use Gson's JsonObject
type.
JsonObject jsonObject = new Gson().fromJson(jsonInput2, JsonObject.class);
String name = jsonObject.get("user") // get the 'user' JsonElement
.getAsJsonObject() // get it as a JsonObject
.get("name") // get the nested 'name' JsonElement
.getAsString(); // get it as a String
System.out.println(name);
which prints
somethingClever
The above method class could have thrown a number of exceptions if they weren't the right type. If, for example, we had done
String name = jsonObject.get("user") // get the 'user' JsonElement
.getAsJsonArray() // get it as a JsonArray
it would fail because user
is not a JSON array. Specifically, it would throw
Exception in thread "main" java.lang.IllegalStateException: This is not a JSON Array.
at com.google.gson.JsonElement.getAsJsonArray(JsonElement.java:106)
at com.spring.Example.main(Example.java:19)
So the JsonElement
class (which is the parent class of JsonObject
, JsonArray
, and a few others) provides methods to check what it is. See the javadoc.
user
is a JsonObject
itself:
JsonObject user = map.get("user");
For people that come here searching for a way to convert LinkedTreeMap to object:
MyClass object = new Gson().fromJson(new Gson().toJson(((LinkedTreeMap<String, Object>) theLinkedTreeMapObject)), MyClass .class)
This was usefull for me when i needed to parse an generic object like:
Class fullObject {
private String name;
private String objectType;
private Object objectFull;
}
But i don't know which object the server was going to send. The objectFull will become a LinkedTreeMap
The JSON string has following structure:
{
created_at: "",
id: "",
user: {
id_str: "",
name: "",
screen_name: ""
}
}
When you put the values in the map using the code:
Map<String, Object> map = new HashMap<String, Object>();
map = (Map<String, Object>) gson.fromJson(jsonInput, map.getClass());
It has following key values:
created_at
id
user
and that's why you are able to use map.get("created_at")
.
Now, since you want to get the name of the user, you need to get the map of user:
LinkedTreeMap<String, Object> userMap = (LinkedTreeMap<String, Object>) map.get("user");
In the userMap
, you would get following key values:
id_str
name
screen_name
Now, you can get the name
of the user
String name = userMap.get("name");