9

Problem Deserializing array as string with Jackson 2

This is a similar problem to Deserialize ArrayList from String using Jackson

The incoming JSON (which I can't control) has an element 'thelist' which is an array. However, sometimes this comes in as an empty string instead of an array:

eg. instead of "thelist" : [ ]
it comes in as "thelist" : ""

I'm having trouble parsing both cases.

The 'sample.json' file which works fine:

{
   "name" : "widget",
   "thelist" : 
    [
       {"height":"ht1","width":"wd1"}, 
       {"height":"ht2","width":"wd2"}
    ]
}

The classes:

public class Product { 
    private String name; 
    private List<Things> thelist; 
    // with normal getters and setters not shown
}

public class Things {
        String height;
        String width;
        // with normal getters and setters not shown
}

The code that works fine:

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Test2 {
 public static void main(String[] args) 
    {
        ObjectMapper mapper = new ObjectMapper(); 
        Product product = mapper.readValue( new File("sample.json"), Product.class);
    }
}

However, when the JSON has got an empty string instead of an array, ie. "thelist" : ""
I get this error:

com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [collection type; class java.util.ArrayList, contains [simple type, class com.test.Things]] from JSON String; no single-String constructor/factory method (through reference chain: com.test.Product["thelist"])

If I add this line (which works for Ryan in Deserialize ArrayList from String using Jackson and seemingly supported by the documentation),

mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);

it makes no difference.

Is there some other setting, or do I need to write a custom deserializer?
If the latter, is there a simple example of doing this with Jackson 2.0.4 ?
I'm new to Jackson (and first time poster, so be gentle). I have done lots of searching, but can't find a good working example.

2 Answers 2

4

Problem is that although single-element-to-Array works, you are still trying a conversion from (empty) String into an Object. I am assuming this is the problem you are facing, although without exception it is hard to say.

But there is also DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT which might do the trick, combined with the first feature. If so, you would get a List with a single "empty Object", meaning Things instance without values.

Now ideally what should happen is that if you only enabled ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, that would probably do what you want: null value for thelist property.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks, but both suggestions still gave the same exception as before, viz: com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [collection type; class java.util.ArrayList, contains [simple type, class com.test.Things]] from JSON String; no single-String constructor/factory method (through reference chain: com.test.Product["thelist"])
So I guess I need to write a custom deserializer? Can you point me to a simple working example of doing this with Jackson 2.0.4 ?
It's actually a non-trivial thing (not impossible, just more work). But I think there may be a simpler way: you can override setThelist method, make it take java.lang.Object (or JsonNode), and then handle cases separately. For empty String you get "", for non-empty, java.util.List (or ArrayNode if using JSON Tree). You will need to use ObjectMapper.convertValue for contents for actual binding. But unless this is a common occurence, it is probably less work than a custom Collection deserializer.
Now, if you do want/need to write custom Collection deserializer, that'd be done by implementing Deserializers that overrides method called to construct deserializer, register it using Module (simple module won't do for Collection handlers). You can have a look at standard implementations from under com.fasterxml.jackson.databind.deser.std -- there are a few special cases to support type info etc.
Looking at [jira.codehaus.org/browse/JACKSON-620], ACCEPT_EMPTY_STRING_AS_NULL_OBJECT does work for arrays, Collections and Maps as well. And it does work with data and class shown above too, with or without the other setting. I am using 2.1.0-SNAPSHOT, but it should not differ from 2.0.4 in this area.
1

Hi I was solving similar problem, when I get object like this

{  
   "name" : "objectname",
   "param" : {"height":"ht1","width":"wd1"}, 
}

from external system so "param" was OBJECT for me which I try to deserilize. When this object was defined in external system it works without problem. But when OBJECT "param" in external system was not defined I get empty ARRAY instead of empty OBJECT

{  
   "name" : "objectname",
   "param" : [], 
}

which cause mapping exception. I solve it with creating custom json deserializer which has very good example here and for testing of type I used something like

    ObjectCodec oc = jsonParser.getCodec();
    JsonNode node = oc.readTree(jsonParser);
    if (JsonNodeType.OBJECT == node.getNodeType()) {
        ParamObject result = new ParamObject();
        result.setHeight(node.get("height").asText());
        result.setWidth(node.get("width").asText());
        return result;
    }
    // object in MailChimp is not defined
    return null;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.