7

I'm using Jackson within CXF to serialize/deserialize data. Unfortunately, I am having difficulty configuring CXF/Jackson to deserialize a JSON array. I'd appreciate help in resolving the issue.

Up to this point most of the json data has been in object format, i.e.

{ "objectCollection": [ {...}, {...}, {...}... ] }

However, the json data in question is of the form:

[ {...}, {...}, {...} ]

The web service endpoint expects a "GroupsDto" object (see following) that has a single property -- a collection of groups, which is transmitted via the JSON array.

@PATH(...)
public Response createGroups(GroupsDto groups) {
...
}

I added @JsonDeserialize as follows to the GroupsDto collection property, but it does NOT work. I continue to get: "Can not deserialize instance of GroupsDto out of START_ARRAY token"

public class GroupsDto {

       private Collection<GroupDto> groups;

       /**
        * @return the groups
        */
       @XmlElement(name="group")
       @JsonDeserialize(contentAs=GroupDto.class)
       public Collection<GroupDto> getGroups() {
               return groups;
       }
...
}
Ari
  • 4,121
  • 8
  • 40
  • 56
  • Check this question [Jackson - reading a JSON array with Robospice using loadDataFromNetwork() method][1] [1]: http://stackoverflow.com/questions/18792702/jackson-reading-a-json-array-with-robospice-using-loaddatafromnetwork-method – Sneg May 16 '14 at 01:59

3 Answers3

9

If json data is of the form:

[ {...}, {...}, {...} ]

You got to use add another class say 'wrapper':

@JsonIgnoreProperties(ignoreUnknown = true)
public class ListDto extends ArrayList<GroupDto> {

    public ListDto() {
    }
}

And use this class while deserailizing. This approach worked for me.

Snehal Masne
  • 3,403
  • 3
  • 31
  • 51
1

You just need to specify the @JsonDeserialize(contentAs=GroupDto.class) in your setter. Serialization is always on get desserialization is always on set, or if you prefer you can specify both on the field.

Documentation for Serialize and Deserialize

Code sample:

import java.io.IOException;
import java.util.List;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonDeserialize;

public class JacksonDeserialize {

    public static class ModelClass {

        private String name;

        public ModelClass() {
        }

        public String getName() {
            return name;
        }

        public void setName(final String name) {
            this.name = name;
        }

        public ModelClass(final String name) {
            super();
            this.name = name;
        }

        @Override
        public String toString() {
            return "ModelClass [name=" + name + "]";
        }

    }

    public static class ListModelClass {

        private List<ModelClass> list;

        @JsonDeserialize(contentAs = ModelClass.class)
        public void setList(final List<ModelClass> list) {
            this.list = list;
        }

        @Override
        public String toString() {
            return "ListModelClass [list=" + list + "]";
        }

    }

    public static void main(final String[] args) throws JsonGenerationException, JsonMappingException, IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        System.out.println(objectMapper.readValue("{\"list\":[{\"name\":\"name1\"},{\"name\":\"name2\"}]}",
                ListModelClass.class));
    }

}
Francisco Spaeth
  • 23,493
  • 7
  • 67
  • 106
  • Thanks for the heads-up on the getter/setter issue. I noticed in your example that you're qualifying the json array i.e. "{\" *list* \":...} Is it possible to deserialize *without* the qualification? If yes, how? – Ari Jun 20 '12 at 16:30
  • there iss no qualification, the magic is done in this case by `@JsonDeserialize` annotation. The property `contentAs` is to indicate that this is a collection and will be deserialized as `ModelClass`. If you want you could change the property name from `list` to something else. The name doesn't matter, what matters is the annotation. – Francisco Spaeth Jun 20 '12 at 18:02
  • I understand that the specific name is immaterial, but what does one do in the case where there is no name? In your example you're reading in `{"list": [...]}`, which has the key 'list', but what if there is no key (name) -- i.e. `[...]`? – Ari Jun 20 '12 at 18:16
  • Ah, there in lies the problem. The serialization/deserialization is handled by CXF, which calls a pre-defined `ObjectMapper`; consequently, I can't simply pass a `TypeReference` to a custom `ObjectMapper`, I have to rely on the pre-defined `ObjectMapper` and annotations. – Ari Jun 20 '12 at 18:25
  • Ok, which library are you using to integrate both? – Francisco Spaeth Jun 20 '12 at 18:52
  • There is no integration library; CXF allows one to specify a predefined handler. – Ari Jun 20 '12 at 20:09
0

Without using wrapper classes

Using Jackson's ObjectMapper

objectMapper.readValue();

Having data as follows...

[... , {YourObj} , {YourObj} , ... ]

You could do

YourObj[] yourObjArray = objectMapper.readValue(source, YourObj[].class);


If you then want to convert it to a List you could further do something like

List<YourObj> yourObjList = Arrays.stream(yourObjArray).collect(Collectors.toList())

Reference https://www.baeldung.com/jackson-collection-array

Omkar Pai
  • 1
  • 2