0

I have a JSON like the following:

{
    systemId: 7
    name: "Phil"
    data: "XYZ"

    pointers: [
        {
            systemId: 23
            name: "Peter"
        },
        {
            systemId: 27
            name: "Jeroen"
        }
    ]
}

In the POJO equivalent of the above JSON, all fields are final. Now, during deserialization, I want to have the ability to modify the systemId field before it is set on the POJO by Jackson. Further, the changed systemId would actually be provided by an stateful object as a function of the current systemId from the JSON. For example, the new systemId to be set on the deserialized POJO might be provided by an instance of a class such as follows:

public class SystemIdProvider {
    ...
    public long getNewSystemId(long oldSystemId) {
        // do something with the oldSystemId and
        // the current state of this object to get the newSystemId
        return newSystemId;
    }
}

Is there a way I can supply an instance of the above class to Jackson when it is deserializing a JSON, and to make Jackson use this object to get the newSystemId before setting it on the POJO's it creates?

Please note that the JSON above is just a small snippet of the actual JSON and hence might contain hundreds of such objects as above. And I want to provide a NEW instance of the SystemIdProvider class per deserialization as the "state" it maintains is also based on the systemIds it has encountered thus far. Thus it needs to start with a clean state for every deserialization.

Any inputs are appreciated.

Skylark
  • 387
  • 2
  • 4
  • 13
  • I think you need to explore some of the options mentioned here for a related, but not same problem - http://stackoverflow.com/questions/7105745/how-to-specify-jackson-to-only-use-fields-preferably-globally – Wand Maker Jul 26 '15 at 09:54
  • thanks Wand Maker. I am already aware of those config options. – Skylark Jul 26 '15 at 12:09

1 Answers1

1

You need to write a custom deserializer for this. Let's assume you have the following POJO:

public class MyPojo {

    private final long systemId;
    private final String foo;

    public MyPojo(long systemId, String foo) {
        this.systemId = systemId;
        this.foo = foo;
    }

    // getters and other methods
}

Write a custom deserializer for the systemId field:

public class SystemIdDeserializer extends StdDeserializer<Long> {

    public SystemIdDeserializer() {
        super(Long.class);
    }

    @Override
    public Long deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        SystemIdProvider systemIdProvider = (SystemIdProvider) ctxt.getAttribute("systemIdProvider");
        Long oldSystemId = _parseLong(jp, ctxt);
        return systemIdProvider.getNewSystemId(oldSystemId);
    }
}

Use the @JsonDeserialize annotation on the systemId field to register the deserializer:

@JsonDeserialize(using = SystemIdDeserializer.class)
private final long systemId;

To make the SystemIdProvider available in the DeserializationContext, call Jackson's mapper like this:

ObjectMapper mapper = new ObjectMapper();
MyPojo deserialized = mapper.reader(MyPojo.class)
        .withAttribute("systemIdProvider", new SystemIdProvider())
        .readValue(json);
hzpz
  • 7,536
  • 1
  • 38
  • 44
  • thank you very much for the answer and the code examples. I need a new instance of SystemIdProvider every time I deserialize a JSON, because it builds its state using the systemIds it has encountered so far in the JSON. It is not a singleton. There arises my problem. I am wondering if I can "inject" it somehow into the DeserializationContext. – Skylark Jul 26 '15 at 12:08
  • Another thing is, I want to use the custom deserialization for just that one field namely systemId instead of the entire class. – Skylark Jul 26 '15 at 13:11
  • thanks a ton hzpz! That looks like exactly the thing I was looking for. I was looking at the injectableValues in the context, never saw the attribute thing. Going to try this out soon. – Skylark Jul 27 '15 at 15:47