Deserialize only one object from JSON array in Java

Viewed 314

I get JSON response from API:

{
  "value": [{
    "some-field": "blah-blah",
    ...
  }]
}

There is always one object in the array so I don't want to store a pojo like this:

public class Foo {
    private List<InnerClass> value;
}

but like this:

public class Foo {
    private String field1;
    private String field2;
...
}

How do I create custom deserializer for it?

I have tried the following:

public class CustomDeserializer extends StdDeserializer<Foo> {

    public CustomDeserializer(Class<Foo> clazz) {
        super(clazz);
    }

    public CustomDeserializer() {
        this(null);
    }

    @Override
    public Foo deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
 
        JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
        String s = jsonNode.get("value").get(0).toString();
        Foo obj = new ObjectMapper().readValue(s, Foo.class);
        return obj;
    }
}

but there is some kind of circle issues and it doesn't work.

1 Answers

The problem comes from this line in you custom deserializer:

Foo obj = new ObjectMapper().readValue(s, Foo.class);

When you do it like this your CustomDeserializer is called a second time, but now with a json in different format and it can't handle it. To be more precise, it can't find the property named value. So you have two options for fixing it:

  1. Create Foo and set fields manually
public class CustomDeserializer extends StdDeserializer<Foo> {

    public CustomDeserializer(Class<Foo> clazz) {
        super(clazz);
    }

    public CustomDeserializer() {
        this(null);
    }

    @Override
    public Foo deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
        JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
        JsonNode valueNode = jsonNode.get("value").get(0);
        Foo obj = new Foo();
        obj.setField1(valueNode.get("field1").asText());
        obj.setField2(valueNode.get("field2").asText());
        return obj;
    }
}
  1. To keep CustomDeserializer as is, you need to register your deserializer only with the ObjectMapper instance, which does the actual deserializing of the initial json and don't use annotations on Foo to specify deserializer.
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Foo.class, new CustomDeserializer());
mapper.registerModule(module);

As a side note, if you choose second option, Foo obj = new ObjectMapper().readValue(s, Foo.class); - this line creates new instance of ObjectMapper every time you deserialize. It would be better to keep an instance of ObjectMapper as a static field, for example.

Related