6

I would like to know if it is possible to customize the deserialization of json depending the field name, for example

{
   id: "abc123",
   field1: {...}
   other: {
        field1: {....}
     }
}

I the previous json, I would like to have a custom deserializer for the fields named "field1", in any level in the json.

The reason: We have our data persisted as JSON, and we have a REST service that returns such data, but before return it, the service must inject extra information in the "field1" attribute.

The types are very dynamic, so we cannot define a Java class to map the json to use annotations.

An first approach was to deserialize to Map.class and then use JsonPath to search the $..field1 pattern, but this process is expensive for bigger objects.

I appreciate any help.

Thanks,

Edwin Miguel

Edwin Miguel
  • 409
  • 4
  • 11
  • This https://stackoverflow.com/a/41002122/2954288 will help if you can and want to annotate the object being read in. – Harald Aug 20 '17 at 06:26

2 Answers2

3

You should consider registering a custom deserializer with the ObjectMapper for this purpose.

Then you should be able to simply map your JSON stream to a Map<String, Object> knowing that your field1 objects will be handled by your custom code.

Costi Ciudatu
  • 37,042
  • 7
  • 56
  • 92
  • Thanks for yor answer, but how Jackson knows when to use the custom deserializer if I mapping to Map?, I mean, there is no way (I think) to know the target object if there is not definition of a own java class. – Edwin Miguel Dec 16 '14 at 15:04
  • I just created a custom deserializer and registered it for Map.class. For now the problem is that I need to handle all the deserialization of the object. – Edwin Miguel Dec 16 '14 at 18:37
  • What I had in mind was that your deserializer would be aware of the position in the document and only took action if the current field was `field1`, otherwise delegate to `super` or whatever. I just hope I didn't suggest an impossible approach... – Costi Ciudatu Dec 17 '14 at 08:40
3

I create a custom deserializer and added it to a SimpleModule for the ObjectMapper

public class CustomDeserializer extends StdDeserializer<Map> {
public CustomDeserializer() {
    super(Map.class);
}

@Override
public Map deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
    Map<String, Object> result = new HashMap<String, Object>();
    jp.nextToken();
    while (!JsonToken.END_OBJECT.equals(jp.getCurrentToken())) {
        String key = jp.getText();
        jp.nextToken();
        if ("field1".equals(key)) {
            MyObject fiedlObj= jp.readValueAs(MyObject.class);
            //inject extra info
            //...
            result.put(key, fieldObj);
        } else {
            if (JsonToken.START_OBJECT.equals(jp.getCurrentToken())) {
                result.put(key, deserialize(jp, ctxt));
            } else if (JsonToken.START_ARRAY.equals(jp.getCurrentToken())) {
                jp.nextToken();
                List<Object> linkedList = new LinkedList<Object>();
                while (!JsonToken.END_ARRAY.equals(jp.getCurrentToken())) {
                    linkedList.add(deserialize(jp, ctxt));
                    jp.nextToken();
                }
                result.put(key, linkedList);
            } else {
                result.put(key, jp.readValueAs(Object.class));
            }
        }
        jp.nextToken();
    }
    return result;
}

}

The problem is that I had to implement the parsing for the remaining attributes.

For now, it is my solution...

Edwin Miguel
  • 409
  • 4
  • 11
  • Can't you delegate to an existing deserializer, rather than write the whole deserialization code yourself ? – Costi Ciudatu Dec 17 '14 at 08:42
  • 1
    I would like to delegate but, I'm not sure how to do that. Maybe extending MapSerializer, but I'm not sure how to pass the required parameters to the constructor. – Edwin Miguel Dec 18 '14 at 16:54
  • "The problem is that I had to implement the parsing for the remaining attributes" -- the _key issue_ is that the function _must_ return `ctxt.handleUnexpectedToken` ... see [https://stackoverflow.com/a/50754025/2979421](https://stackoverflow.com/a/50754025/2979421) – Ron Sep 18 '18 at 23:07