0

I need to deserialize some json which can contain either an array of objects [{},{}] or a single object {}. See my question. Here is what I'm trying to do :

    public class LocationDeserializer extends JsonDeserializer<List<Location>>{

    @Override
    public List<Location> deserialize(JsonParser jp,
        DeserializationContext ctxt) throws IOException
    {
        List<Location> list = new ArrayList<Location>();
        if(!jp.isExpectedStartArrayToken()){
            list.add(...);
        }else{
            //Populate the list
        }

        return list;
    }

But I'm getting stuck here. How can I remap the object? And how to tell Jackson to use this deserializer for the attribute "location"?

Here is how the Json can look :

{

"location":
    [
        {
            "code":"75",
            "type":"1"
        },
        {
            "code":"77",
            "type":"1"
        }
    ]
}

or

{
"location":
        {
            "code":"75",
            "type":"1"
        }
}
Community
  • 1
  • 1
TrtG
  • 2,778
  • 6
  • 26
  • 39

2 Answers2

0

I don't know what your JSON looks like, but I think using ObjectNode is a lot easier for this case than using JsonDeserializer. Something like this:

ObjectNode root = mapper.readTree("location.json");
if (root.getNodeType() == JsonNodeType.ARRAY) {
  //Use a get and the JsonNode API to traverse the tree to generate List<Location>
}
else {
  //Use a get and the JsonNode API to traverse the tree to generate single Location or a one-element List<Location>
}
Vidya
  • 29,932
  • 7
  • 42
  • 70
  • Ok this might be a better start. I have added some json examples to my question. Now how would you populate the list or the Location object in both case? And also how to tell jackson to use this parser for the location attribute of my parent object? – TrtG Jan 12 '14 at 12:28
  • First, it is up to you. You could generate a single `Location` in the `else` case or a one-element collection. The latter might be neater. Second, it isn't the most elegant, but I would configure your REST endpoint to take the request as plain text and then manually deserialize the request body with simlar code. With `JsonDeserializer`, you normally annotate the appropriate POJO, but I don't know how it applies with a (possible) collection. – Vidya Jan 13 '14 at 19:01
0

You can tell Jackson to use this deserializer with the Annotation JsonDeserialize.

And inside your deserialize method, you could use the following:

@Override
public List<Location> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
    List<Location> list = new ArrayList<Location>();
    ObjectMapper mapper = new ObjectMapper();
    JsonNode root = mapper.readTree(jp);
    if(root.get("location").isArray()){
        // handle the array
    }else{
        // handle the single object
    }

    return list;
}
Martin Seeler
  • 6,874
  • 3
  • 33
  • 45
  • I've tried this and I get a ClientProtocolException : Could not extract response: no suitable HttpMessageConverter found for response type [com.app.model.PositionResponse] and content type [application/json]. Here is the annotation I've set : @JsonDeserialize(using = LocationDeserializer.class) public void setLocation(final List list) { this.location = list; } – TrtG Jan 12 '14 at 13:40
  • Well, this does not belong to this question, thats an error you have in another part of your code where you, I guess, recieve The JSON via Apache HttpClient. But this part should work. – Martin Seeler Jan 12 '14 at 15:39
  • I think it belongs to it because the exception is raised when I put the annotation on the setter. So it is a part of "how to tell Jackson to use this deserializer for the attribute "location"". Should I update my question with more code? – TrtG Jan 12 '14 at 16:22
  • The annotation is for the Location class, so you would write `@JsonDeserialize(using = LocationDeserializer.class) public class Location ...`, not the deserialize method. – Martin Seeler Jan 12 '14 at 19:26