1

Background:

I am getting mismatchedInputException, when parsing json. com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type java.util.ArrayList<Data> from Object value (token JsonToken.START_OBJECT)

Problem:

nodes is a json-array which contains data, which can be either json-object or json-array .

I need to represent this correctly in POJO.

Things Tried: Map<String, Object>, List, ,@JsonDeserialize(using = NodeDeserializer.class)

Below is my nested json document for ref.

{
  "parent": {
    "nodes": [
      {
        "data": {
          "desc": "a1",
          "url": "http://a1.a11"
        }
      },
      {
        "data": [
          {
            "desc": "b1",
            "url": "http://b1.b11"
          },
          {
            "desc": "b2",
            "url": "http://b2.b21"
          }
        ]
      }
    ]
  }
}

Below is Pojo Java classes and Runner class, I am using for SER/DE for above Json

Data.java

import lombok.*;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
@JsonInclude(JsonInclude.Include.NON_NULL)
class Data {
    @JsonProperty("desc")
    private String desc;

    @JsonProperty("url")
    private String url;
}

Node.java

import lombok.*;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
@JsonInclude(JsonInclude.Include.NON_NULL)
class Node {
    @JsonProperty("data")
    //Map<String, Object> data = new LinkedHashMap<>();
    //private List<Object> data;
    private List<Data> data;
}

Parent.java

import lombok.*;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
@JsonInclude(JsonInclude.Include.NON_NULL)
class Parent {
    @JsonProperty("nodes")
    private List<Node> nodes;
}

Test.java

import lombok.*;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Test {
    @JsonProperty("parent")
    private Parent parent;
}

Runner Class

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

public class TestJsonParse {
    public static void main(String[] args) throws JsonProcessingException {
        String jsonString = "{\n" +
                "  \"parent\": {\n" +
                "    \"nodes\": [\n" +
                "      {\n" +
                "        \"data\": {\n" +
                "          \"desc\": \"a1\",\n" +
                "          \"url\": \"http://a1.a11\"\n" +
                "        }\n" +
                "      },\n" +
                "      {\n" +
                "        \"data\": [\n" +
                "          {\n" +
                "            \"desc\": \"b1\",\n" +
                "            \"url\": \"http://b1.b11\"\n" +
                "          },\n" +
                "          {\n" +
                "            \"desc\": \"b2\",\n" +
                "            \"url\": \"http://b2.b21\"\n" +
                "          }\n" +
                "        ]\n" +
                "      }\n" +
                "    ]\n" +
                "  }\n" +
                "}";

        // print pojo
        ObjectMapper objectMapper = new ObjectMapper();
        Test test = objectMapper.readValue(jsonString, Test.class);
        System.out.println(test);
    }
}
axnet
  • 5,146
  • 3
  • 25
  • 45

1 Answers1

1

How to write common pojo deserializer for json attribute which can be an object and an array both?

It is possible if you use the JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY deserialization feature consisting of an override for the DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY feature which allows deserialization of JSON non-array values into single-element Java arrays and Collections. So you can annotate the data field of your Node class to obtain the expected behaviour:

class Node {
    @JsonProperty("data")
    @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
    private List<Data> data;
}
dariosicily
  • 4,239
  • 2
  • 11
  • 17
  • Thanks dariosicily, it is working, but the only issue is it is treating Single Json Object as a List containing only one object. – axnet Oct 02 '22 at 11:15
  • @praxnet You are welcome, the behaviour is correct because `data` field has a `List` type so jackson automatically transforms the single object into a list of one object. – dariosicily Oct 02 '22 at 14:28
  • @darioicily, though this is working in standalone json deserialization, but when i use it with spring boot rest controller and fire json post request from postman, i am again getting exception, is there any special setting for spring-boot which i am missing? ----> Cannot deserialize value of type `java.util.ArrayList` from Object value (token `JsonToken.START_OBJECT`) at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 9, column: 17] (through reference chain: – axnet Oct 04 '22 at 08:49
  • @praxnet `spring.jackson.deserialization.accept-single-value-as-array=true` should be a good match, [here](https://stackoverflow.com/questions/39041496/how-to-enforce-accept-single-value-as-array-in-jacksons-deserialization-process) are alternatives depending the jackson version your spring boot project is using. – dariosicily Oct 04 '22 at 09:35
  • using 2.7.3 spring boot and it is having jackson-databind 2.13.3 which is almost new, and i am using annotation based only, not specified config in application.properties, is it also needed along with Annotation JsonFormat? – axnet Oct 04 '22 at 09:46
  • 1
    @praxnet I suggested it because this is the simplest method to add features to the mapper your project is using, check the link I previously added adapting one of the solutions proposed to your case. – dariosicily Oct 04 '22 at 10:18
  • thanks dariosicily, its working with my case now. – axnet Oct 04 '22 at 10:19