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.

Community
  • 1
  • 1
user1721784
  • 91
  • 1
  • 1
  • 2

2 Answers2

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.

StaxMan
  • 113,358
  • 34
  • 211
  • 239
  • 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"]) – user1721784 Oct 09 '12 at 00:29
  • 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 ? – user1721784 Oct 09 '12 at 00:32
  • 1
    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. – StaxMan Oct 09 '12 at 16:47
  • 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. – StaxMan Oct 09 '12 at 16:49
  • Looking at [https://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. – StaxMan Oct 09 '12 at 23:45
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;
Community
  • 1
  • 1
Mr.B
  • 91
  • 2