Parse json with variable key - java

I just came up with challenging problem.
Below is json response where key is variable (a GUID)
How can I parse it? I've tried Google Gson, but that didn't work.
{
"87329751-7493-7329-uh83-739823748596": {
"type": "work",
"status": "online",
"icon": "landline",
"number": 102,
"display_number": "+999999999"
}
}

If you use Gson, in order to parse your response you can create a custom class representing your JSON data, and then you can use a Map.
Note that a Map<String, SomeObject> is exactly what your JSON represents, since you have an object, containing a pair of string and some object:
{ "someString": {...} }
So, first your class containing the JSON data (in pseudo-code):
class YourClass
String type
String status
String icon
int number
String display_number
Then parse your JSON response using a Map, like this:
Gson gson = new Gson();
Type type = new TypeToken<Map<String, YourClass>>() {}.getType();
Map<String, YourClass> map = gson.fromJson(jsonString, type);
Now you can access all the values using your Map, for example:
String GUID = map.keySet().get(0);
String type = map.get(GUID).getType();
Note: if you only want to get the GUID value, you don't need to create a class YourClass, and you can use the same parsing code, but using a generic Object in the Map, i.e., Map<String, Object>.

Related

Serialize to JSON as name-value from String by Jackson ObjectMapper

I have some String, like:
String value = "123";
And when i serialize this string to json via ObjectMapper:
objectMapper.writeValueAsString(value);
Output is:
"123"
Is it possible to write String using either string name and string value? Desired output:
"value" : "123"
PS: i dont want to create DTO object with one field for serializing one String value.
you can also use the Jackson JsonGenerator
try (JsonGenerator generator = new JsonFactory().createGenerator(writer)) {
generator.writeStartObject();
generator.writeFieldName("value");
generator.writeString("123");
generator.writeEndObject();
}
}
If you have a plain string you'll get out a plain string when serialised. If you want to wrap it in an object then use a map for the simplest solution.
String value = "123";
Map<String, String> obj = new HashMap<>();
obj.put("value", value);
Passing that through the mapper will produce something like this:
{ "value": "123" }
If you change the map to <String, Object> you can pass in pretty much anything you want, even maps within maps and they'll serialise correctly.
If you really can't have the enclosing curly braces you can always take the substring but that would be a very weird use case if you're still serialising to JSON.
Create a Map:
Map<String, String> map = new HashMap<>();
map.put("value", value);
String parsedValue = ObjectMapper.writeValueAsString(map);
and you will get: {"value":"123"}
If you are using java 8 and want to do it in automated way without creating maps or manually putting string variable name "value", this is the link you need to follow-

How to convert HashMap of Object and String[] to json

Below is my code :
public static void main(String[] args) {
HashMap<HierarchyFilter, String[]> filters = new HashMap();
HierarchyFilter obj = new HierarchyFilter("name1", "type1", "value1");
String[] a = new String[6];
a[0]="String1";
a[1]="String2";
a[2]="String3";
a[3]="String4";
a[4]="String5";
a[5]="String6";
filters.put(obj, a);
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(filters);
System.out.println(jsonString);
}
I am using jackson mapper. But my object is not getting converted properly.
Below is the out put what i receive:
{
"com.remote.HierarchyFilter#63bd725" : [ "String1", "String2", "String3", "String4", "String5", "String6" ]
}
I receive the default toString of the Object.
please help
If you are expecting Jackson to map the "com.remote.HierarchyFilter#63bd725" to JSON with the values "name1", "type1", "value1", then you are expecting an INVALID json, which Jackson mapper wouldn't do.
The JSON map data structure is a JSON object data structure, which is a collection of name/value pairs, where the element names must be strings. The JSON map keys must also be strings because JSON map is a JSON object. So, when you try use HierarchyFilter key, it uses the toString method (a string value) of the object to use it as Key.
TO achieve what you need to send to UI, decide on a proper JSON structure contract and then design your Object classes/response objects accordingly.

Deserialize nested JSON array in Java with Gson

Something like this. http://jsonlint.com/ says it is valid. Json inside {} simplified for this example.
[[0,{"ok":true},[]],[1,{"ok":false},[]]]
Or with indents:
[
[0, {
"ok": true
},
[]
],
[1, {
"ok": false
},
[]
]
]
This is class for object JSONClass.
public class JSONClass {
boolean ok;
}
If I got it right, this JSON string is array of arrays, latter containing some ID, actual JSON data and empty array. How could I deserialize that?
This doesn't work. I also tried making class with subclasses, didn't work out.
JSONClass[] t = g.fromJson(json, JSONClass[].class);
Well, you have an array of arrays here. Gson will let you convert the JSON objects themselves into the class you want - but you'll have to call gson.fromJson() on each of them separately.
Given String json containing your json, something like this should work:
Gson gson = new Gson();
JsonParser jsonParser = new JsonParser();
JsonArray jsonArray = jsonParser.parse(json).getAsJsonArray();
for (JsonElement e: jsonArray) {
JSONClass o = gson.fromJson(e.getAsJsonArray().get(1), JSONClass.class);
}
Essentially, the JsonParser will convert your text into a JsonElement, which is the Gson base class for Json arrays and objects. We can iterate over the elements of the JsonArray which we parsed our text into, which in turn is another array of the format [id, object] - and for each element, take the object portion, and deserialize that into a POJO.

Jackson Unmarshall custom object instead of LinkedHashMap

I have a Java object Results:
public class MetaData {
private List<AttributeValue<String,Object>> properties
private String name
...
... getters/setters ...
}
The AttributeValue class is a generic key-value class. It's possible different AttributeValue's are nested. The (value) Object will then be another AttributeValue and so forth.
Due to legacy reasons the structure of this object cannot be altered.
I have my JSON, which I try to map to this object.
All goes well for the regular properties. Also the first level of the list is filled with AttributeValues.
The problem is the Object. Jackson doesn't know how to interpret this nested behavior and just makes it a LinkedHashMap.
I'm looking for a way to implement custom behavior to tell Jackson this has to be a AttributeValue-object instead of the LinkedHashMap.
This is how I'm currently converting the JSON:
ObjectMapper om = new ObjectMapper();
MetaData metaData = om.readValue(jsonString, new TypeReference<MetaData>(){});
And this is example JSON. (this is obtained by serializing an existing MetaData object to JSON, I have complete control over this syntax).
{
"properties":[
{
"attribute":"creators",
"value":[
{
"attribute":"creator",
"value":"user1"
},{
"attribute":"creator",
"value":"user2"
}
]
},{
"attribute":"type",
"value": "question"
}
],
"name":"example"
}
(btw: I've tried the same using GSON, but then the object is a StringMap and the problem is the same. Solutions using GSON are also welcome).
edit In Using Jackson ObjectMapper with Generics to POJO instead of LinkedHashMap there is a comment from StaxMan:
"LinkedHashMap is only returned when type information is missing (or if Object.class is defined as type)."
The latter seems to be the issue here. Is there a way I can override this?
If you have control over the serialization, try calling enableDefaultTyping() on your mapper.
Consider this example:
Pair<Integer, Pair<Integer, Integer>> pair = new Pair<>(1, new Pair<>(1, 1));
ObjectMapper mapper = new ObjectMapper();
String str = mapper.writeValueAsString(pair);
Pair result = mapper.readValue(str, Pair.class);
Without enableDefaultTyping(), I would have str = {"k":1,"v":{"k":1,"v":1}} which would deserialize to a Pair with LinkedHashMap.
But if I enableDefaultTyping() on mapper, then str = {"k":1,"v":["Pair",{"k":1,"v":1}]} which then perfectly deserializes to Pair<Integer, Pair<...>>.

Gson, JSON and the subtleties of LinkedTreeMap

I've recently started playing around with JSON strings, and was told that Google's own library, Gson, is the new and hip way of dealing with these.
The way I've understood it, is that a JSON string is essentially a map. Where each variable points to a value in the string.
For example:
String jsonInput2 = "{\"created_at\":\"Sat Feb 08 15:37:37 +0000 2014\",\"id\":432176397474623489\"}
Thus far, all is well. Information such as when this JSON string was created, can be assigned to a variable with the following code:
Gson gson = new Gson();
Map<String, String> map = new HashMap<String, String>();
map = (Map<String, String>) gson.fromJson(jsonInput, map.getClass());
String createdAt = map.get("created_at");
It's almost artistic in in simple beauty. But this is where the beauty ends and my confusion begins.
The following is an extension of the above JSON string;
String jsonInput2 = "{\"created_at\":\"Sat Feb 08 15:37:37 +0000 2014\",\"id\":432176397474623489\",\"user\":{\"id_str\":\"366301747\",\"name\":\"somethingClever\",\"screen_name\":\"somethingCoolAndClever\"}}";
My question is how these "brackets within brackets" work for the user section of the JSON?
How could I assign the values specified within these inner-brackets to variables?
Can anyone explain to me, or show me in code, how Gson handles stuff like this, and how I can use it?
In short, why does...
String jsonInput = "{\"created_at\":\"Sat Feb 08 15:37:37 +0000 2014\",\"id\":432176397474623489\",\"user\":{\"id_str\":\"366301747\",\"name\":\"somethingClever\",\"screen_name\":\"somethingCoolAndClever\"}}";
Gson gson = new Gson();
Map<String, String> map = new HashMap<String, String>();
map = (Map<String, String>) gson.fromJson(jsonInput, map.getClass());
String name = map.get("name");
System.out.println(name);
... print out null?
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.
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");
user is a JsonObject itself:
JsonObject user = map.get("user");
Ok. First of all JSON is short for "JavaScript Object Notation" so your assertion that "a JSON string is essentially a map" is incorrect. A JSON block is an object graph, described using the JavaScript language syntax. Since your trying to coerce an object graph to a Map of String, Sting kay value pairs, this is only going to work in cases where any given JSON object graph is essentially just that (so not very often). A more successful strategy would probably be gson.fromJson() which will convert your JSON to a proper Java object graph.

Categories