GnxR
GnxR

Reputation: 839

Deserialize into dynamic Java types

My problem today is that I have this kind of JSON, with an array of objects, each object having two properties: type and value.

[{
    "type": "Boolean",
    "value": false
}, {
    "type": "String[]",
    "value": ["one", "two", "three"]
}]

As you see, the value class depends on the type. I may have a type: MyClass, with value being a complex object.

I want to be able to deserialize this array (and serialize it back into JSON afterwards).

I started looking into the Gson library, but I may change anytime. From what I've read, there is only one way I know of:

public class MyExtractor implements JsonDeserializer<Object> {
    @Override
    public Object deserialize(JsonElement elem, Type jtype,
            JsonDeserializationContext jdc) throws JsonParseException {
        // elem is an object
        JsonObject obj = elem.getAsJsonObject();
        String type = obj.get("type").getAsString();
        // Return a different kind of Object depending on the 'type' attr
        switch (type) {
        case "Boolean":
            Boolean bool = obj.get("value").getAsBoolean();
            return bool;
        case "String[]":
            JsonArray data = obj.get("value").getAsJsonArray();
            List<String> list = new ArrayList<String>();
            for (JsonElement item : data) {
                list.add(item.getAsString());
            }
            return list;
        }
    }
}

And here I add code to associate each type to its proper class. This will probably work as I want, but is there a better way? This one ends up requiring quite a lot of template.

Also, the deserialized items are cast into Object so I don't have access to their methods without reading their getClass() and casting them back, and I can't benefit from overloading.

Are there libraries with different approaches to the problem?

Upvotes: 2

Views: 1654

Answers (3)

Dariusz
Dariusz

Reputation: 22271

pretius-jddl is capable of deserialization based on the existence or value of particular fields. The example in readme is almost exactly what you want.

In your case you'd have to create two rules that distinguish deserialization depending on the value of type:

DynamicObjectDeserializer deserializer = new DynamicObjectDeserializer();

deserializer.addRule(DeserializationRuleFactory.typeByFieldValue(1, 
    "type", "Boolean", Boolean.class));
deserializer.addRule(DeserializationRuleFactory.typeByFieldValue(1, 
    "type", "String[]", String[].class));

There's more information on this blog post about jddl

Upvotes: 0

Davide Lorenzo MARINO
Davide Lorenzo MARINO

Reputation: 26926

If You can change the original json from:

[{
    "type": "Boolean",
    "value": false
}, {
    "type": "String[]",
    "value": ["one", "two", "three"]
}]

to

[false, ["one", "two", "three"]]

You can simply deserialize it as

String jsonString = ...
Object[] result = new Gson().fromJson(jsonString, Object[].class);

This work well for each standard type (String, Boolean, Integer...), for custom classes it will create a Map.


For example if the jsonString has content

["testString", false, {"name":"Davide", "surname":"Marino"}]

it will generate an Object array with:

  • the String testString in first position
  • the Boolean false in second position
  • the Map with properties name (with value Davide) and surname (value Marino) in third position

If You don't need to handle existing java classes, but you have 3 4 custom classes you can use a polimorphic deserialization.

Basically you need to create a custom interface (or base class) and implement (or extend) it. To see a more complete example see the accepted answer of

Gson serialize a list of polymorphic objects

Upvotes: 2

Alexander.Furer
Alexander.Furer

Reputation: 1869

You can try jolt json transformer and then use one of the standard mappers (Jackson, gson ect)

Upvotes: 1

Related Questions