1

There's a json string without key for each objects like

[
    {
        "name": "A",
        "number": 1,
        "age": 20
    },
    {
        "name": "B",
        "number": 2,
        "age": 30
    },
    {
        "name": "C",
        "number": 3,
        "age": 40
    }
]

and I only need name and number, so I have a class like below trying to encode the json string into it

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@EqualsAndHashCode(callSuper = true)
public class FooResClass extends BaseResModel {

 private static final long serialVersionUID = -6398045272254450504L;

 private List<AData> aDataList;

  @Data
  @JsonIgnoreProperties(ignoreUnknown = true)
  public class AData {
    @JsonProperty("number")
    private Long number;
    @JsonProperty("name")
    private String name;
  }

then it says

jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of

am I doing anything wrong?

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
chiefpika
  • 427
  • 1
  • 4
  • 13

1 Answers1

1

TL;DR

Your JSON and POJO don't match, and there are several ways to resolve the problem:

  • parse JSON as List<FooResClass.AData> and then instantiate FooResClass;
  • define a custom Deserializer (that allows to reuse the parsing logic and would be a preferred way if you would choose to change the code);
  • change the structure of your JSON.

Details

Firstly, if AData is nested, it should be static (otherwise an instance of the enclosing class would be required for instantiation and parsing would fail).

Secondly, the JSON you have represents an Array of JSON-objects that correspond to the AData class. I.e. it's not a JSON-object but a JSON-array, which matches a List<AData>, but not to the instance of enclosing class FooResClass.

For instance, the following parsing would succeed:

String json = // your json data
ObjectMapper mapper = new ObjectMapper();

List<AData> aData = mapper.readValue(json,  new TypeReference<List<FooResClass.AData>>(){});
FooResClass fooRes = new FooResClass(aDataList); // assuming that all-args constructor was defined

In order to obtain an instance of FooResClass with your current code, the JSON should be structured in the following way:

{
   "aDataList":[
      {
         "name":"A",
         "number":1,
         "age":20
      },
      {
         ...
      },
      {
         ...
      }
   ]
}

If you can't change the structure of the JSON you need to customize the deserialization of the FooResClass.

Deserializer

For that, you can define a custom Deserializer by extending StdDeserializer and overriding its abstract method deserialize().

That's how it might be implemented:

public class FooResClassDeserializer extends StdDeserializer<FooResClass> {

    public FooResClassDeserializer() {
        this(null);
    }

    public FooResClassDeserializer(Class<FooResClass> vc) {
        super(vc);
    }

    @Override
    public FooResClass deserialize(JsonParser p,
                                   DeserializationContext ctxt) throws IOException, JacksonException {
        
        ObjectMapper mapper = new ObjectMapper();
        List<FooResClass.AData> aDataList = mapper.readValue(p, new TypeReference<>() {});
        return new FooResClass(aDataList);
    }
}

To instruct Jackson that this Desirializer should be used while pasing FooResClass, you need to annotate this class with @JsonDeserialize and specify the class of Desirializer thought the using attribute.

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
@JsonDeserialize(using = FooResClassDeserializer.class)
public static class FooResClass extends BaseResModel {

    private static final long serialVersionUID = -6398045272254450504L;

    @JsonProperty("aDataList")
    private List<AData> aDataList;

    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class AData {
        @JsonProperty("number")
        private Long number;
        @JsonProperty("name")
        private String name;
    }
}

With this change, you'll be able to parse the current unaltered JSON into an instance of FooResClass (I've tested this solution by removing the extends clause, since you haven't provided BaseResModel class).

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
  • The json data is from another api which I can't modify. – chiefpika Dec 09 '22 at 11:52
  • I'm trying to implement the Deserializer you mentioned now.. thanks. – chiefpika Dec 09 '22 at 11:53
  • @chiefpika I've tested the Desirelizer posted in the Answer against your sample data, and it works fine. It would require some changes only if `BaseResModel` declares properties that you expect to come in the form of JSON. If it's not the case, you can use it as is. – Alexander Ivanchenko Dec 09 '22 at 11:59
  • the "new TypeReference<>()" says "Diamond operator is not applicable for non-parameterized types", I followed everything, not sure if anything went wrong. – chiefpika Dec 09 '22 at 12:20
  • @chiefpika Which version of Java you're using? Try to provide the type explicitly `new TypeReference>() {}`. – Alexander Ivanchenko Dec 09 '22 at 12:27
  • Java 8 because of my team... have no choice. It shows`Type 'jdk.internal.org.objectweb.asm.TypeReference' does not have type parameters` after changing it into `new TypeReference>() {}` – chiefpika Dec 09 '22 at 12:32
  • @chiefpika Imported `TypeReference` is the wrong one. It should come from the package `com.fasterxml.jackson.core.type`, **not** `jdk.internal.org.objectweb.asm`. – Alexander Ivanchenko Dec 09 '22 at 12:35