I just released Module 10 in the Master Class of “REST with Spring”:

>> THE "REST WITH SPRING" CLASSES

1. Overview

This quick tutorial will illustrate how to use Jackson 2 to deserialize JSON using a custom Deserializer.

If you want to dig deeper and learn other cool things you can do with the Jackson 2 – head on over to the main Jackson tutorial.

2. Standard Deserialization

Let’s start by defining 2 entities and see how Jackson will deserialize a JSON representation to these entities without any customization:

public class User {
    public int id;
    public String name;
}
public class Item {
    public int id;
    public String itemName;
    public User owner;
}

Now, let’s define the JSON representation we want to deserialize:

{
    "id": 1,
    "itemName": "theItem",
    "owner": {
        "id": 2,
        "name": "theUser"
    }
}

And finally, let’s unmarshall this JSON to Java Entities:

Item itemWithOwner = new ObjectMapper().readValue(json, Item.class);

3. Custom Deserializer on ObjectMapper

In the previous example, the JSON representation matched the java entities perfectly – next, we will simplify the JSON:

{
    "id": 1,
    "itemName": "theItem",
    "createdBy": 2
}

When unmarshalling this to the exact same entities – by default, this will of course fail:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: 
Unrecognized field "createdBy" (class org.baeldung.jackson.dtos.Item), 
not marked as ignorable (3 known properties: "id", "owner", "itemName"])
 at [Source: [email protected]; line: 1, column: 43] 
 (through reference chain: org.baeldung.jackson.dtos.Item["createdBy"])

We’ll solve this by doing our own deserialization with a custom Deserializer:

public class ItemDeserializer extends StdDeserializer<Item> { 

    public ItemDeserializer() { 
        this(null); 
    } 

    public ItemDeserializer(Class<?> vc) { 
        super(vc); 
    }

    @Override
    public Item deserialize(JsonParser jp, DeserializationContext ctxt) 
      throws IOException, JsonProcessingException {
        JsonNode node = jp.getCodec().readTree(jp);
        int id = (Integer) ((IntNode) node.get("id")).numberValue();
        String itemName = node.get("itemName").asText();
        int userId = (Integer) ((IntNode) node.get("createdBy")).numberValue();

        return new Item(id, itemName, new User(userId, null));
    }
}

As you can see, the deserializer is working with the standard Jackson representation of JSON – the JsonNode. Once the input JSON is represented as a JsonNode, we can now extract the relevant information from it and construct our own Item entity.

Simply put, we need to register this custom deserializer and simply deserialize the JSON normally:

ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Item.class, new ItemDeserializer());
mapper.registerModule(module);

Item readValue = mapper.readValue(json, Item.class);

4. Custom Deserializer on the Class

Alternatively we can also register the deserializer directly on the class:

@JsonDeserialize(using = ItemDeserializer.class)
public class Item {
    ...
}

With the deserializer defined at the class level, there is no need to register it on the ObjectMapper – a default mapper will work fine:

Item itemWithOwner = new ObjectMapper().readValue(json, Item.class);

This type of per-class configuration is very useful in situations in which we may not have direct access to the raw ObjectMapper to configure.

5. Conclusion

This articles shows how to leverage Jackson 2 to read non-standard JSON input – and how to map that input to any java entity graph with full control over the mapping.

The implementation of all these examples and code snippets can be found in my github project – this is an Eclipse based project, so it should be easy to import and run as it is.

Go deeper into building a REST API with Spring:

>> CHECK OUT THE COURSE

  • Bug: The second JSON should contain `createdBy` instead of `owner`…

    • Nice catch – I updated the article with the fix.
      Thanks,
      Eugen.

  • Anamika

    My custom deserializer is not getting invoked. Do you know why this can happen. I am using Fasterxml jackson.

    • Hi Anamika – can you please provide some code to look at (preferably on github) – without that, it’s hard to say why your deserializer isn’t invoked. Cheers,
      Eugen.

  • When working with legacy systems, the attribute names of the JSON sometimes dont match the field names of a POJO. Additionally, sometimes we’ll want to ignore particular JSON fields altogether. Are there annotations/techniques for allowing for this without having to hand code your own deserialiser?

  • Denis

    I get null on jp.getCodec(). Any idea?

    • Hey Denis – well, there may be any number of reasons, so I’ll need a code sample – preferably a test I can run. Alternatively you can work backwards from my working example over on github and figure out what’s not working when you replace my entity with your own. Regardless – working code sample would make things easier. Cheers,

      Eugen.

      • Denis

        HI Eugen – Thank you for your quick reply. I use MongoJack to map the Json (Bson) from MongoDB to my objects. I still don’t know why I get the null from getCodec() but when I use jp.readValueAsTree() it works. See this example/issue https://github.com/jroper/mongojack/issues/1 – Cheers, Denis

        • Hey Denis – so I took a quick look at that project – unfortunately it looks like getting even that test to work inside mongojack is a lenghty task (it doesn’t currently compile) – and it also look like even the original reporter gave up :). My suggestion is this – try to replicate the null (jp.getCodec()) in a working test without other frameworks involved. If it’s simply a Jackson problem – that should not be problematic – however if it’s a mongojack problem – then that may be worth following up there. If you do have a working test – do follow up with me – either here or on github – and it will be my pleasure to help. Cheers,
          Eugen.

  • bsengar

    Hi,
    I would like to deserialize a JSON array with different
    node names to Java object array. Any idea how can I do that with
    Jackson.
    For example, JSON looks like this

    “65796”: {
    “name”: “Harrow West”,
    “country”: “E”,
    “type”: “WMC”},

    “11820”: {
    “name”: “Brent and Harrow”,
    “country”: “E”,
    “type”: “LAC”},

    “66065”: {
    “name”: “Brent Central”,
    “country”: “E”,
    “type”: “WMC”},

    Thanks

    • First, the json is missing the start and end to be valid – I’m assuming you’re adding these portions when you run it through Jackson. Second, you should be able to register a custom deserializer (covered here) and then just deserialize the collection. Hope it helps. Cheers,
      Eugen.

  • Srini

    Is it possible to have a deserializer work for this case- I have two classes MyClass and Myclass wrapped up in another class say MyClassWrapper. The incoming stream to my application could be json of either MyClass or MyClassWrapper(can’t know till we parse the stream). Can you let me know how can I have single deserializer to handle these? I don’t have much understanding of internals of json deserializing, would be great if you could provide some pointers on this. Thanks.

    • Hey Srini – so, there are 2 things you need to pay attention to. One – looking at the json, how can you differentiate between the raw class and the wrapped class. And two – in Java, I’m assuming these both extend a common base class (or at least implement a common interface).
      With that in mind, your custom deserialization code would return that common class/interface and, in the implementation – would instantiate the right variant based on the json.
      Hope that helps. Cheers,
      Eugen.

  • DJ

    I’m having an odd issue. When i run my unit/integration tests my Custom Deserializer creates an Object node and the objects get created properly; however when I post the same JSON from the Web it creates a TextNode and does not parse. Followed the recommended setup…

    SimpleModule module = new SimpleModule();

    module.addDeserializer(DataFieldBinderList.class, new DataFieldBinderDeserializer());

    mapper.registerModule(module);

    binder = mapper.readValue(s, DataFieldBinderList.class);

    • Hey DJ – not sure I can diagnose this here – if you have a test reproducing the issue – maybe somewhere on github, I’ll be happy to take a look. Cheers,
      Eugen.

  • Matthew

    I am trying to deserialise JSON into a large complex model of generated code (i.e. I cannot annotate or change the model). the vast majority of fields match the incoming json so that Jackson will map them by default, but a few fields have minor naming differences. is there a way of implementing a custom Deserializer that only needs to handle the exception cases, whilst Jackson’s default mapping does the majority of the work?

    • Hmm – that’s an interesting question. As far as I know – not exactly – meaning not a custom deserializer. But maybe – depending on how complex the changes need to be – you can simply use annotations to change the names of properties on the setters. This way Jackson will map these correctly and you won’t need a whole new deserializer.
      Hope it helps. Cheers,
      Eugen.

  • Swapnil Daga

    Thanks a Lot ! This Works great !

  • gerbalife

    Thanx Eugene for article. But I have a problem.
    Custom serailizer and deserializer are not registered via class level annotations, only by adding to SimpleModule which is registered in ObjectMapper.
    Why it happens?

    • I’m not sure I fully understand the question – can you elaborate? Also, a good way to ask this kind of code-specific and to the point question is via StackOverflow – then just add the link in the comment here – and I’ll have a look over on SO. Cheers,
      Eugen.

      • gerbalife

        Should have delete this question, cause find out a solution.
        I have some classes that implement interface. Serializer and deserializer classes are built using interface class, but annotations were used in its implementations. Since I moved annotations to interface everything works

        • No worries – glad everything worked out. Cheers,
          Eugen.

  • stallapp

    Nice articles.

    Can I make my custom ValueInstantiator in deserialization process as default value instantiator?

    • Not sure I follow – which part of the article are you referring to exactly? Cheers,
      Eugen.

      • stallapp

        The question is not directly related to any article on this website but Jackson deserialization concept. While json deserialization Jackson uses default StdValueInstantiator to create an instance of the respective. I want to implement ValueInstantiator and make it as default. I am not sure whether it is possible or not.

        • I see – that makes sense.
          But, seeing how the question isn’t relevant to this particular article, feel free to email me instead – just to keep the comments here focused. Cheers,
          Eugen.

  • Raul Mordillo Lluva

    Hi,
    I came across your post, it was very helpful, thanks!
    I wanted to point out that the reference API says this:
    “Custom deserializers should usually not directly extend this class, but instead extend StdDeserializer”.
    Thanks for your posts!

    • Hey Raul – that’s a good point, I’ll have a look. Cheers,
      Eugen.

  • Jyothi Rajesh

    Hi,

    I am trying to use the JsonDeserilize annotation. I have added it to a setter method. The setter method takes an interface. My JsonDeserializer implementation gives out the interface only as output. Why does jackson give me the “abstract types can only be instantiated with additional type information” error even if I have defined a custom deserializer? Ideally it should just call my custom deserializer and proceed.

    Thanks and Regards,
    Jyothi

    • Hey Jyothi,
      When Jackson deserializes something, it needs to have some way of determining what class you’re expecting to get on the other end. There are a few ways to signal that information to the library. But – bottom line is that it needs that information in order to perform the process. And that’s because, even though your method signature is an interface, the actual object that gets returned from that method will still have to have a concrete class at runtime. So – that’s the missing part in Jackson – how does it know what that concrete class is.
      Hope that clarifies things.
      Cheers,
      Eugen.

  • Alex

    Thanks for the post Eugen.

    Could you advice with next issue please? Large JSON is coming over network in chunks(strings). The size of chunk is fixed as a result I could receive a few full records and part of a record. Is this possible to parse valid records, save unparsed tail of string to parse it later when next chunk will arrive?

    • Hey Alex,
      Jackson does have a streaming API – that would be the first place I’d look. I actually haven’t run into that exact usecase when I used the streaming API (a while back), so I can’t say for sure.
      But, given that it’s definitely an interesting usecase, I added it to the Content Calendar of the site – so keep an eye on the feed, it should go out in a couple of months.
      Cheers,
      Eugen.

  • Grant Walker

    Thanks for the post Eugen.

    Is it possible to though to Deserialize an object that contains a list of objects of the same type? What is mean is, Im trying to deserialize a filter that has a list of filters within it.
    here is the JSON:
    {
    “logic”: “and”,
    “filters”: [
    { “field”: “isMale”, “operator”: “eq”, “value”: true },
    {
    “logic”: “or”,
    “filters”: [
    { “field”: “firstName”, “operator”: “eq”, “value”: “Bryan” },
    { “field”: “firstName”, “operator”: “eq”, “value”: “Grant” }
    ]
    }
    ]
    }

    This is the object:
    class Filter {
    String logic;
    String field;
    String operator;
    String value;
    List filters;
    }

    The reason I want to use a custom deserializer is that I cannot control the type of the ‘value’ attribute when the JSON string is created and I need to determine if its String, int, boolean or other.

    Thanks in advance,
    Grant

    • Grzegorz Piwowarek

      I am not sure if I fully understand. Serializing/Deserializing objects nested like this will not be a problem but I do not really understand the problem with the value field. So a filter can hold a value of any type?

      • Grant Walker

        For now, lets ignore the value type issue, I think I found a way around that. How can I deserialize the Filter object when it contains a list of Filter objects in a custom Deserializer

        • Chaoqiang Tao

          I am suffering same question, so have you found certain solusion?

          • Grant Walker

            I actually didnt have to do anything special. When I ran the object through a Jackson deserializer it deserialized the ‘value’ to string regardless of the type passed in, and it deserialized the list within without a problem. So I didnt actually need a custom deserializer.

  • Pake

    Nice post Eugen 🙂

    Which is the correct way to make a custom deserializer for an object which have a field that need another custom deserializer?

    For example, for this json:

    {
    “name”:”Chelsea”,
    “abrev”:”CH”,
    “date”:”2016-10-15T00:00:00″,
    “players”:[{
    “id”: 1,
    “name”: “Costa”,
    “birth”: “2016-10-15T00:00:00”
    }]
    }

    I have a custom deserializer for the Player POJO that manage de date (format, etc). But now I want to make another custom deserializer for Team POJO, but I don’t know how to specify in the TeamDeserializer that each Player need to be deserialized throught the PlayerDeseralizer.

    Thanks a lot!

    • Hmm – that’s an interesting scenario Pake – I haven’t tried to do that before. If you can have a (minimal) example set up somewhere accessible, I’d be happy to have a look.
      Cheers,
      Eugen.

  • kenyee

    this all seems so painful compared to Gson and LoganSquare 😛

    • That’s an interesting point. You have to keep in mind that this is the custom way of doing things, which – naturally – is more complex. But you only need the custom way if all the simpler ways failed.
      Most of the deserialization mechanism in Jackson are much simpler than this one.
      Hope that clears things up. Cheers,
      Eugen.