Eugene
Eugene

Reputation: 60184

Can not deserialize JSON with Jackson lib

I have a JSON:

{
    "firstField": "Something One",
    "secondField": "Something Two",
    "thirdField": [
        {
            "thirdField_one": "Something Four",
            "thirdField_two": "Something Five"
        },
        {
            "thirdField_one": "Something Six",
            "thirdField_two": "Something Seven"
        }
    ],
    "fifthField": [
        {
            "fifthField_one": "Something… ",
            "fifthField_two": "Something...",
            "fifthField_three": 12345
        },
        {
            "fifthField_one": "Something",
            "fifthField_two": "Something",
            "fifthField_three": 12345
        }
    ]
}

I have my classes:

public static class MyClass {
        @JsonProperty
        private String firstField, secondField;
        @JsonProperty
        private ThirdField thirdField;
        @JsonProperty
        private FifthField fifthField;

        public static class ThirdField {
            private List<ThirdFieldItem> thirdField;
        }

        public static class ThirdFieldItem {
            private String thirdField_one, thirdField_two;
        }

        public static class FifthField {
            private List<FifthFieldItem> fifthField;
        }

        public static class FifthFieldItem {
            private String fifthField_one, fifthField_two;
            private int fifthField_three;
        }
    }

I'm deserializing them with Jackson library:

public void testJackson() throws IOException {
    JsonFactory factory = new JsonFactory();
    ObjectMapper mapper = new ObjectMapper(factory);
    File from = new File("text.txt"); // JSON I mentioned above
    mapper.readValue(from, MyClass.class);
}

but I'm getting the Exception:

org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of Main$MyClass$ThirdField out of START_ARRAY token

Upvotes: 2

Views: 2711

Answers (2)

bdoughan
bdoughan

Reputation: 148977

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.

Since it is not always possible to change your domain model (as answered by @Perception), below is how you could map your original object model to the desired JSON using MOXy.

Java Model

In this use case you can leverage the @XmlPath(".") extension. This tells MOXy to bring the contents of the target object into the sources node.

@XmlAccessorType(XmlAccessType.FIELD)
public static class MyClass {
    private String firstField, secondField;

    @XmlPath(".")
    private ThirdField thirdField;

    @XmlPath(".")
    private FifthField fifthField;

    @XmlAccessorType(XmlAccessType.FIELD)
    public static class ThirdField {
        private List<ThirdFieldItem> thirdField;
    }

    @XmlAccessorType(XmlAccessType.FIELD)
    public static class ThirdFieldItem {
        private String thirdField_one, thirdField_two;
    }

    @XmlAccessorType(XmlAccessType.FIELD)
    public static class FifthField {
        private List<FifthFieldItem> fifthField;
    }

    @XmlAccessorType(XmlAccessType.FIELD)
    public static class FifthFieldItem {
        private String fifthField_one, fifthField_two;
        private int fifthField_three;
    }
}

Conversion Code

The demo code below shows how to enable MOXy's JSON binding.

public static void main(String[] args) throws Exception {
    Map<String, Object> properties = new HashMap<String, Object>(2);
    properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
    properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
    JAXBContext jc = JAXBContext.newInstance(new Class[] {MyClass.class}, properties);

    Unmarshaller unmarshaller = jc.createUnmarshaller();
    StreamSource json = new StreamSource("src/forum13600952/input.json");
    MyClass myClass = unmarshaller.unmarshal(json, MyClass.class).getValue();

    Marshaller marshaller = jc.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    marshaller.marshal(myClass, System.out);
}

input.json/Output

Belos is the input from your question slightly reformatted to match the output produced by MOXy.

{
   "firstField" : "Something One",
   "secondField" : "Something Two",
   "thirdField" : [ {
      "thirdField_one" : "Something Four",
      "thirdField_two" : "Something Five"
   }, {
      "thirdField_one" : "Something Six",
      "thirdField_two" : "Something Seven"
   } ],
   "fifthField" : [ {
      "fifthField_one" : "Something...",
      "fifthField_two" : "Something...",
      "fifthField_three" : 12345
   }, {
      "fifthField_one" : "Something",
      "fifthField_two" : "Something",
      "fifthField_three" : 12345
   } ]
}

For More Information

Upvotes: 0

Perception
Perception

Reputation: 80593

You defined your thirdField and fifthField properties as arrays in your JSON. They need to be arrays or collections on your Java bean as well:

public static class MyClass {
    @JsonProperty
    private String firstField, secondField;

    @JsonProperty
    private Collection<ThirdField> thirdField;

    @JsonProperty
    private Collection<FifthField> fifthField;

    /// ...
}

As you are going through and converting an existing JSON object into beans, keep in mind that JSON data is very much like a map. If you envision how you would map the data from a map into your object it really helps. Your ThirdField and FifthField objects need to map the definitions in your JSON. This is what your JSON says a ThirdField is:

{
    "thirdField_one": "Something Four",
    "thirdField_two": "Something Five"
}

Literally converting that to a Java bean gives you:

public class ThirdField implements Serializable {
    private String thirdField_one;
    private String thirdField_two;

    // ...
}

You can add in your annotations etc, etc to get a full fledged bean. Do the same thing for your FifthField object.

Upvotes: 5

Related Questions