I am using Jackson to deserialize a JSON. The Jackson has to handle 2 things:
- Identify if the provided JSON is a document and if so deserialize the elements in
CustomerList
one by one. - Identify if the provided JSON is a single
Customer
and if so then deserialize thecustomer
directly.
I am able to achieve this and everything is working as expected but when I provide the CustomerList
document then it's unable to read the @Context
key-value pair.
Following is the JSON
i am trying to deserialize:
{
"@context": [
"https://stackoverflow.com",
{
"example": "https://example.com"
}
],
"isA": "CustomerDocument",
"customerList": [
{
"isA": "Customer",
"name": "Batman",
"age": "2008"
}
]
}
Following is my Customer POJO class:
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Customer implements BaseResponse {
private String isA;
private String name;
private String age;
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonSubTypes({
@JsonSubTypes.Type(value = Customer.class, name = "Customer")})
interface BaseResponse {
}
Following is the Main:
public class JacksonMain {
public static void main(String[] args) throws IOException {
final InputStream jsonStream = JacksonMain.class.getClassLoader().getResourceAsStream("Customer.json");
final JsonParser jsonParser = new JsonFactory().createParser(jsonStream);
final ObjectMapper objectMapper = new ObjectMapper();
jsonParser.setCodec(objectMapper);
//Goto the start of the document
jsonParser.nextToken();
try {
BaseResponse baseResponse = objectMapper.readValue(jsonParser, BaseResponse.class);
System.out.println("SINGLE EVENT INPUT" + baseResponse.toString());
} catch (Exception e) {
System.out.println("LIST OF CUSTOMER INPUT");
//Go until the customerList has been reached
while (!jsonParser.getText().equals("customerList")) {
System.out.println("Current Token Name : " + jsonParser.getCurrentName());
if (jsonParser.getCurrentName() != null && jsonParser.getCurrentName().equalsIgnoreCase("@context")) {
System.out.println("WITHIN CONTEXT");
}
jsonParser.nextToken();
}
jsonParser.nextToken();
//Loop through each object within the customerList and deserilize them
while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
final JsonNode customerNode = jsonParser.readValueAsTree();
final String eventType = customerNode.get("isA").asText();
Object event = objectMapper.treeToValue(customerNode, BaseResponse.class);
System.out.println(event.toString());
}
}
}
}
When I run the application I get the following response:
LIST OF CUSTOMER INPUT
Current Token Name : isA
Customer(isA=Customer, name=Batman, age=2008)
As we can see it's printing only Current Token Name: isA
I would expect it to print isA and @Context
because it's present before the isA
.
Now if I remove the following code I have on the interface BaseResponse
:
@JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "isA")
Then I am able to read the @context
but that would result in following error:
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `stackover.BaseResponse` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
I tried various things such as adding the following line:
@JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "@context")
But nothing seems to work as expected for me. Can someone please suggest some work-around on how to make it work?
I tried to create a copy of the Jackson JsonParser
but even that does not seem to work for me.
Please Note:
The
CustomerList
can have a lot ofCustomers
hence I do not want to store the wholeCustomerList
into someList
as it can take a lot of memories. Hence, I am usingJsonParser
so I can read oneJsonToken
at a time.Also, I do not want to create a
CustomerList
class rather than that I want to read one Customer at a time and deserialize it.