Balázs Németh
Balázs Németh

Reputation: 6637

Deserialize JSON by property

I have a simple wrapper class.

class Wrapper {
    int id;
    Object command;
}

command could be an object that I get from the outside, and I cannot create an interface to hold the possible types together. I'd like to serialize it simply:

String json = objectMapper.writeValueAsString(wrapper);

So that I get:

{"id":"1","command":{"type" : "objectType", "key0": "val0", ... other properties...}}

Ideally I'd build a registry with the possible values of type and the corresponding class names as values, so I could deserialize it like this:

Wrapper wrapper = objectMapper.readValue(bytes, Wrapper.class);

(objectMapper is com.fasterxml.jackson.databind.ObjectMapper)

Is there a way to achieve this with Jackson?

Upvotes: 1

Views: 270

Answers (2)

Alexey Gavrilov
Alexey Gavrilov

Reputation: 10853

You can use the Jackson polymorphic type handling. You can declare which type the command property can be using @JsonTypeXXX annotations.

Here is a complete example:

public class JacksonTypeInfoOnObject {

    public static class Bean {
        @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
        @JsonSubTypes({
                @JsonSubTypes.Type(Command1.class),
                @JsonSubTypes.Type(Command2.class)
        })
        public final Object command;

        @JsonCreator
        public Bean(@JsonProperty("command") final Object command) {this.command = command;}

        @Override
        public String toString() {
            return "Bean{" +
                    "command=" + command +
                    '}';
        }
    }

    @JsonTypeName("cmd1")
    public static class Command1 {
        @Override
        public String toString() {
            return "Command1{}";
        }
    }

    @JsonTypeName("cmd2")
    public static class Command2 {
        @Override
        public String toString() {
            return "Command2{}";
        }
    }


    public static void main(String[] args) throws IOException {
        final ObjectMapper mapper = new ObjectMapper();
        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        final List<Bean> list = Arrays.asList(
                new Bean(new Command1()),
                new Bean(new Command2()));
        final String json = mapper.writeValueAsString(list);
        System.out.println(json);
        final List<Bean> values = mapper.readValue(json, new TypeReference<List<Bean>>() {});
        System.out.println(values);
    }
}

Output:

[{"command":{"type":"cmd1"}},{"command":{"type":"cmd2"}}]
[Bean{command=Command1{}}, Bean{command=Command2{}}]

Upvotes: 1

Mr. Polywhirl
Mr. Polywhirl

Reputation: 48640

I changed the type of your command property to a Map<String, Object> and the Wrapper object can be serialized/deserialized as expected.

Below, is the output generated by the Main class:

SERIALIZE:   {"id":1,"command":{"key0":"val0","type":"objectType"}}
DESERIALIZE: Wrapper [id=1, command={key0=val0, type=objectType}]

Main.java

package json;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Main {
    static ObjectMapper objectMapper = new ObjectMapper();

    private static Wrapper createWrapper() {
        Wrapper wrapper = new Wrapper();
        Map<String, Object> command = new HashMap<String, Object>();

        command.put("type", "objectType");
        command.put("key0", "val0");

        wrapper.id = 1;
        wrapper.command = command;

        return wrapper;
    }

    private static String serializeWrapper(Wrapper wrapperObj) {
        try {
            return objectMapper.writeValueAsString(wrapperObj);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static Wrapper deserializeWrapper(String wrapperJsonStr) {
        try {
            return objectMapper.readValue(wrapperJsonStr, Wrapper.class);
        } catch (JsonParseException e) {
            e.printStackTrace();
        } catch (JsonMappingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    public static void main(String[] args) {
        Wrapper wrapper = createWrapper();

        String wrapperJsonStr = serializeWrapper(wrapper);
        System.out.printf("SERIALIZE:   %s%n", wrapperJsonStr);

        Wrapper wrapperObj = deserializeWrapper(wrapperJsonStr);
        System.out.printf("DESERIALIZE: %s%n", wrapperObj);
    }
}

Wrapper.java

package json;

import java.util.Map;

public class Wrapper {
    public int id;
    public Map<String, Object> command;

    @Override
    public String toString() {
        return "Wrapper [id=" + id + ", command=" + command + "]";
    }
}

Upvotes: 0

Related Questions