Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserializing attributes of same name but different types in Jackson?

I have a REST API which returns a JSON response as:

{
    "channel" : "JHBHS"
}

and sometimes it returns:

{
    "channel": {
                    "id": 12321,
                    "name": "Some channel"
               }
}

I have a POJO like:

public class Event {
    private String channel;
    @JsonProperty("channel")
    private Channel channelObj;
}

public class Channel {
    private int id;
    private String name;
}

So, is there a way (other than writing your own custom deserializer) in Jackson2 which will help me map channel in JSON to String type when it's a String and Channel type when it's a JSON Object?

Or in other words, is there a way in Jackson which maps by type of the variable and not just by name?

like image 272
Ram Patra Avatar asked Jun 14 '16 13:06

Ram Patra


People also ask

What is the difference between JsonProperty and JsonAlias?

@JsonProperty can change the visibility of logical property using its access element during serialization and deserialization of JSON. @JsonAlias defines one or more alternative names for a property to be accepted during deserialization.

Can Jackson ObjectMapper be reused?

ObjectMapper is the most important class in Jackson API that provides readValue() and writeValue() methods to transform JSON to Java Object and Java Object to JSON. ObjectMapper class can be reused and we can initialize it once as Singleton object.

What is JsonProperty annotation?

@JsonProperty is used to mark non-standard getter/setter method to be used with respect to json property.

What is default typing in Jackson?

Default typing is a mechanism in the Jackson ObjectMapper to deal with polymorphic types and inheritance. If you want to deserialize JSON to a Java POJO but are unsure what subtype the object or the field is, you can simply deserialize to the superclass.


1 Answers

I can suggest you to use JsonNode like this:

class Event {

    @JsonProperty("channel")
    private JsonNode channelInternal;

    private Channel channel;

    private String channelStr;

    /**
     * Custom getter with channel parsing
     * @return channel
     */
    public Channel getChannel() {
        if (channel == null && channelInternal != null) {
            if (channelInternal.isObject()) {
                int id = channelInternal.get("id").intValue();
                String name = channelInternal.get("name").asText();
                channel = new Channel(id, name);
            }
        }
        return channel;
    }

    /**
     * Custom getter with channel string parsing
     * @return channel string
     */
    public String getChannelStr() {
        if (channelStr == null && channelInternal != null) {
            if (channelInternal.isTextual()) {
                channelStr = channelInternal.asText();
            }
        }
        return channelStr;
    }
}

or like this:

class Event {

    private Channel channel;

    private String channelStr;

    @JsonSetter("channel")
    public void setChannelInternal(JsonNode channelInternal) {
        if (channelInternal != null) {
            if (channelInternal.isTextual()) {
                channelStr = channelInternal.asText();
            } else if (channelInternal.isObject()) {
                int id = channelInternal.get("id").intValue();
                String name = channelInternal.get("name").asText();
                channel = new Channel(id, name);
            }
        }
    }

    public Channel getChannel() {
        return channel;
    }

    public String getChannelStr() {
        return channelStr;
    }
}

But I think using custom deserializer is more common.

Here is test code

public static void main(String[] args) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    String source1 = "{\n" +
            "    \"channel\" : \"JHBHS\"\n" +
            "}";
    String source2 = "{\n" +
            "    \"channel\": {\n" +
            "                    \"id\": 12321,\n" +
            "                    \"name\": \"Some channel\"\n" +
            "               }\n" +
            "}";

    //Test object parsing
    Event result = objectMapper.readValue(source2, Event.class);
    System.out.println(String.format("%s : %s", result.getChannel().getId(), result.getChannel().getName()));

    //Test string parsing
    result = objectMapper.readValue(source1, Event.class);
    System.out.println(result.getChannelStr());
}

And the output:

12321 : Some channel
JHBHS
like image 128
Vadeg Avatar answered Sep 19 '22 11:09

Vadeg