38

Is there a way to use annotation on a List property in a class to use ACCEPT_SINGLE_VALUE_AS_ARRAY in Jackson? I'm using Spring and getting the below exception

nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token

Assume I have a class as below:

public class MyClass {

    private List < String > value;
}

And my JSON structures are as below:

case 1:

[{"operator": "in", "value": ["Active"], "property": "status"}]

case 2:

[{"operator": "like", "value": "aba", "property": "desc"}]

What annotation should I use to let the framework know I want these 2 cases to be treated the same when deserializing.

UPDATE: I moved the updates to an answer in this post for more clarity.

4
  • JSON property names are not in double quotes, I believe it is a typo, right? Commented Aug 19, 2016 at 15:29
  • @MichalFoksa, Thanks. I updated the JSON data. Commented Aug 19, 2016 at 15:32
  • Nice and neat solution. I am happy I could help. I suggest you make a new answer out of it with description of your particular circumstances (2.6.x, ...) so that people can better find. Commented Aug 19, 2016 at 19:47
  • is there a way to make object behaviour List<MyClass> or MyClass depending on input json node. i.e. if its JsonNode we deserialize and write json using MyClass. And if its ArrayNode, we deserialize and write json using List<MyClass> or MyClass[] Commented Oct 4, 2022 at 10:01

5 Answers 5

73

You can use @JsonFormat annotation,

public class MyClass {

    @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
    private List<String> value;

}

To work with this you need to have Jackson version min 2.7.0. You can also use other available JsonFormat Features

For version 2.6.x

@Autowired private ObjectMapper mapper;
//...

mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
  • Add this code to your Initializer Class.
  • Or you can directly configure Jackson in your Bean Configuration

These would solve the issue but it will be activated for every deserialization process.

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

4 Comments

I tried your solution but its not working. I have jackson annotation 2.6.0. I'm getting same exception
@Vahid I tested for 2.6.x, its not working as you say, but for 2.7.x and higher its working as you expected, it could be a bug for 2.6.x but i am not sure because they have this functionality in their documentation, if your project depended on 2.6.x , we can find a way to enable this functionality in 2.6.x.
Thanks for confirming. Due to the project dependency restrictions I cannot upgrade. For now I'm going to fix it using a custom deserializer. I'll post my solution in an update.
I didn't try but I think I can get hold of it.
12

I'm just answering my own question for clarity. One of the answers was to upgrade to the higher version so that I can use annotations. I cannot do it due to dependency restrictions of my project.

As a result, based on Michal Foksa answer I solved my problem by creating a custom deserializer. Its as below:

On my property:

@JsonDeserialize(using = CustomStringDeserializer.class)
private List<String> value;

And my Deserializer:

public class CustomStringDeserializer extends JsonDeserializer<List<String>>{

    @Override
    public List<String> deserialize(JsonParser p, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(DeserializationFeature. ACCEPT_SINGLE_VALUE_AS_ARRAY);
        return mapper.readValue(p, List.class);
    }

}

1 Comment

I would accept this answer If it is still correct one, so that people can see which one is actual solution.
7

I would create a custom JsonDeserializer where I would deal with both cases and annotate value class property with the deserializer.

Deserializer:

public class StringListDeserializer extends JsonDeserializer<List<String>>{

    @Override
    public List<String> deserialize(JsonParser parser, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {

        List<String> ret = new ArrayList<>();

        ObjectCodec codec = parser.getCodec();
        TreeNode node = codec.readTree(parser);

        if (node.isArray()){
            for (JsonNode n : (ArrayNode)node){
                ret.add(n.asText());
            }
        } else if (node.isValueNode()){
            ret.add( ((JsonNode)node).asText() );
        }
        return ret;
    }
}

MyClass:

public class MyClass {
    .
    @JsonDeserialize(using = StringListDeserializer.class)
    private List<String> value;
    .
}

Hope it helps.

BTW: If there is a way how to deal with such a case with just an annotation, I also want to know it.

1 Comment

Thanks for pointing to the right direction. I solved my problem using your idea of custom deserializer. In the deserialize method we can fix the issue simpler than your method. Please check my update on the post.
5

For spring projects adding

spring.jackson.deserialization.accept-single-value-as-array=true 

in property files will work for all deserializations handled by spring even on then controller class methods where @RequestBody for the APIs.

Comments

1

If this project is a Spring project then you can put this property in you application.properties:

spring.jackson.deserialization.UNWRAP_SINGLE_VALUE_ARRAYS=true

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.