Reputation: 19905
Is it possible to convert an array of object pairs into a Map
, by selecting values from each pair, without creating an intermediate POJO?
For example, say that there are 2 Java
classes (Key.class
, Value.class
) and a JSON
array constructed from serialized pairs of their objects, as follows
[
{"key":{}, "value":{}}, // Objects k1, v1
{"key":{}, "value":{}}, // Objects k2, v2
{"key":{}, "value":{}}, // Objects k3, v3
{"key":{}, "value":{}}, // Objects k4, v4
]
Can Gson
"directly" (i.e., without a POJO definition) deserialize the above array into a Map
, in which the actual keys and values are populated from a manipulation of the corresponding Key
and Value
objects, instead of the "full" objects?
In other words, given two Java
types K
and V
that may be different to Key
and Value
, the deserialization per pair should be
(k1:Key, v1:Value) --> (k1':K, v1':V)
where
k1' = function(k1)
v1' = function(v1)
A custom adapter like this does not work:
GsonBuilder builder = new GsonBuilder();
Type type = (new TypeToken<Map<K, V>>() {}).getType();
builder.registerTypeAdapter(type, new JsonDeserializer<Map<Key, Value>>() {
@Override
public Map<K, V> deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
// Custom deserialization block,
// invoking the `function()` method
}
Gson gson = builder.create();
Using the above adapter results in Gson
passing the entire array to the deserialize()
method, where it has to be manipulated manually, which is both tedious and error-prone.
With a custom POJO
like
public class KeyValue {
Key k;
Value v;
}
the above JSON
array can be deserialized into an array or set of KeyValue
objects, which can then be converted to a Map<K, V>
. But could the same be done without the KeyValue
POJO;
Upvotes: 1
Views: 1452
Reputation: 4713
If you want to avoid having the POJO class you refer to as KeyValue
I think the only way to do that with gson is to create an adapter. The reason being that your JSON does not conform to any generic, built-in class and therefore gson is not going to be able to understand it without having the custom logic to do so.
Since you have the Key
and Value
classes you can take advantage of that inside of your custom adapter. I think you will find that this reduces the tediousness of the code.
public static void main(String[] args) throws IOException {
GsonBuilder builder = new GsonBuilder();
Type type = (new TypeToken<Map<Key, Value>>() {}).getType();
builder.registerTypeAdapter(type, new JsonDeserializer<Map<Key, Value>>() {
@Override
public Map<Key, Value> deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
Map<Key, Value> resultMap = new HashMap<>();
JsonArray jArr = json.getAsJsonArray();
for(int i=0; i < jArr.size(); i++){
JsonObject item = jArr.get(i).getAsJsonObject();
processKeyValue(item, resultMap);
}
return resultMap;
}
private void processKeyValue(JsonObject mapEntry, Map<Key, Value> resultMap){
Gson gson = new Gson();
Key key = gson.fromJson(mapEntry.get("key"), Key.class);
Value value = gson.fromJson(mapEntry.get("value"), Value.class);
resultMap.put(key, value);
}
});
Gson gson = builder.create();
//Note: assume that the variable "json" is a JSON string. I removed my code for getting the string since I'm just reading from a file and I don't know how you're getting your data.
// Deserialization
Map<Key,Value> outputMap = gson.fromJson(json, type);
System.out.println("RESULTS: "+outputMap.toString());
}
I used your example data, but slightly modified to have a little more content. I created two very simple POJO classes for Key
and Value
- they only have a String
field named id
, a getter and setter for that field, and a toString
method.
This was the input I used when running the above code:
[
{"key":{"id":"k1"}, "value":{"id":"v1"}},
{"key":{"id":"k2"}, "value":{"id":"v2"}},
{"key":{"id":"k3"}, "value":{"id":"v3"}},
{"key":{"id":"k4"}, "value":{"id":"v4"}}
]
The output is:
RESULTS: {Key [id=k2]=Value [id=v2], Key [id=k1]=Value [id=v1], Key [id=k3]=Value [id=v3], Key [id=k4]=Value [id=v4]}
Upvotes: 1