I am using the Jackson library to deserialize JSON. In the JSON I have a few custom fields whose values can be anything, so I am trying to use the @JsonAnySetter
and @JsonAnyGetter
to obtain the values. The fields and values within the JSON can be duplicated and I would like to obtain everything from the JSON and store it within the map. However, the direct Jackson derealization is storing the last value if there are duplicates in the key.
I have a large JSON file that has many events. I am reading the file event-by-event so that the whole JSON file is not stored within memory. After reading a single event, I check the type of event, based on which I assign it to a different POJO. Following is my sample JSON file consisting of 2 events.
[
{
"isA": "Type1",
"name": "Test",
"foo": "val1",
"foo": "val2",
"bar": "val3",
"foo": {
"myField": "Value1",
"myField": "value2"
}
},
{
"isA": "Type2",
"name": "Test1",
"foo": "val1",
"foo": "val2",
"bar": "val3",
"foo": {
"myField": "Value1",
"myField": "value2"
}
}
]
Following is the class that is used for deserialization: (I have many fields which are mapped directly during the deserialization and working correctly so omitted for simplicity). This is the class for the type1
event if it's type2
@JsonInclude(JsonInclude.Include.NON_NULL)
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Type1
{
private String name;
private Map<String, Object> userExtensions;
@JsonAnyGetter
public Map<String, Object> getUserExtensions() {
return userExtensions;
}
@JsonAnySetter
public void setUserExtensions(String key, Object value) {
System.out.println("KEY : " + key + " VALUES : " + values);
}
}
As you can observe from above I have a Map
field that is used to populate the extensions using the JsonAnySetter
. I have a method which will be called for the fields which cannot be directly searlized by Jackson.
I tried setting the System.out
within the @JsonAnySetter
method and I observed that Jackson does not get the duplicate field within this method:
@JsonAnySetter
public void setUserExtensions(String key, Object value) {
System.out.println(" Key : " + key + " Value : " + value);
}
I get only the last fields in this. For the above mentioned JSON I get only the last foo
which has the value:
"foo" :{
"myField" : "Value1"
"myField" : "value2"
}
The first 2 foo
with val1
and val2
does not even print within this method.
Following is my Main
method which is actually the culprit as I am using the objectMapper.treeToValue
of Jackson which does not support the duplicate fields.
public class Main
{
public static void main (String[]args)
{
//File where the JSON is stored
InputStream jsonStream = Main.class.getClassLoader().getResourceAsStream("InputEPCISEvents.json");
final JsonFactory jsonFactory = new JsonFactory();
final JsonParser jsonParser = jsonFactory.createParser (jsonStream);
jsonParser.setCodec (new ObjectMapper ());
final ObjectMapper objectMapper = new ObjectMapper();
// Loop until the end of the events file
while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
// Get the node
final JsonNode jsonNode = jsonParser.readValueAsTree();
// Get the eventType
final String eventType = jsonNode.get("isA").toString().replaceAll("\"", "");
switch (eventType) {
case "Type1":
final Type1 objInfo = objectMapper.treeToValue(jsonNode, Type1.class);
break;
case "Type2":
final Type2 objInfo = objectMapper.treeToValue(jsonNode, Type2.class);
break;
default:
System.out.println("None of the event Matches");
break;
}
}
}
}
I wanted to know how can I make Jackson read the duplicate key values so that I can handle them within the @JsonAnySetter
method.
I wanted to know if there is a way to handle these scenarios directly using Jackson or I need to build my own custom deserialization. If yes, then how can I build one for only one field within the class?
PS: I tried many things available online but none worked hence posting the same. If found duplicate then I am really sorry.
I noticed that most of the code sample on Stack Overflow read JSON with the Jackson readValue
method, but in my case I have a large JSON file that has many events so I am splitting them event by event and using the Jackson objectMapper.treeToValue
method to map it to my class. Within this class, I try to map the fields of each event with the respective class fields and if no match found then I am expecting them to be populated using the @JsonAnySetter
method.