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?
CodePudding user response:
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 instantiateFooResClass
; - 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).