2

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?

GnxR
  • 829
  • 1
  • 8
  • 20
  • You can try taking a look at [dynamic code generation](https://stackoverflow.com/questions/4166135/dynamic-code-execution). – ack Nov 06 '17 at 16:19
  • Can you change the json? – Davide Lorenzo MARINO Nov 06 '17 at 16:19
  • @DavideLorenzoMARINO Yeah, I have access to the JavaScript which generates and uses this JSON. In theory I can do anything with it. In the JS, it's convenient to have all the objects in a single array, but this can change (merging multiple arrays is quite easy in JS). This would make deserialization easier I guess. For now the 'type' attribute is relevant in the unique-array, since there are 4 different "complex classes" with custom properties and behaviors. – GnxR Nov 06 '17 at 16:32

3 Answers3

2

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

Davide Lorenzo MARINO
  • 26,420
  • 4
  • 39
  • 56
1

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

Alexander.Furer
  • 1,817
  • 1
  • 16
  • 24
0

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

Dariusz
  • 21,561
  • 9
  • 74
  • 114