I have class that looks like this:
public class Data<U> {
@JsonProperty("difficulties")
private U[] data;
// ... geter setter constructor
}
And I don't want to create 10 more similar classes just because I need to change only one line of code (@JsonProperty("difficulties") in this case). The property value depends on Type. Is it possible to write it in one class?
Based on response of Jackson - Modify an attribute at runtime without annotation by Michał Ziober here I was able to change default field name values by overriding PropertyNamingStrategy:
These are my received JSON examples (simplified):
{"status": "OK","error": null,"data": {
"difficulties": [{"value":"easy"},{"value":"medium"}]
}}
{"status": "ok", "error": null, "data": {
"countries": [{"code": "AT"},{"code": "BE"}]
}}
see the difference in second line where data object contains either difficulties or countries (or many other names based on context).
Response class based on JSON response:
public class Response<T>{
private String status;
private String error;
private Data<T> data;
// Getters Setters Constructors
}
Data class based on JSON response:
public class Data<T> {
// property name, that will be changed
@JsonProperty(DataNamingStrategy.DATA_FIELD)
private T[] data;
// Getters Setters Constructors
}
And this is Naming strategy, that changes default value to runtime specified value
public class DataNamingStrategy extends PropertyNamingStrategy{
// used by other classes (this will be default field name that should be changed)
public static final String DATA_FIELD = "variable:data";
private String fieldName;
public DataNamingStrategy(String fieldName) {
this.fieldName = fieldName;
}
// use this to change field name (format "variable":"value") not needed in my case
@Override
public String nameForField(MapperConfig<?> config, AnnotatedField field,
String defaultName) {
return (defaultName.equals(DATA_FIELD))?
fieldName :
super.nameForField(config, field, defaultName);
}
// use this to change setter method field name (JSON -> Object with format "variable":{})
@Override
public String nameForSetterMethod(MapperConfig<?> config,
AnnotatedMethod method, String defaultName) {
return (defaultName.equals(DATA_FIELD))?
fieldName :
super.nameForGetterMethod(config, method, defaultName);
}
// use this to change getter method field name (Object -> JSON with format "variable":{})
// should be same as nameForSetterMethod
@Override
public String nameForGetterMethod(MapperConfig<?> config,
AnnotatedMethod method, String defaultName) {
return nameForSetterMethod(config, method, defaultName);
}
}
And usage should look like this:
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(new DataNamingStrategy(tableName));
JavaType type = mapper.getTypeFactory().constructParametricType(Response.class, dataClass);
Response<U> u = mapper.readValue(result, type);
Where result is Json as String, tableName is String that will be used in JSON instead of default value and dataClass is class for U (for example Difficulty.class).
Better usage of PropertyNamingStrategy should be Map instead of one String. But I just needed to change one particular value.
Also have a look at PropertyNamingStrategy documentation or again at Michał Ziober's answer
You can use @JsonAnyGetter annotation.
public class Data<U> {
@JsonIgnore
private U[] data;
@JsonIgnore
private String propertyName;
public Data(String propertyName) {
this.propertyName = propertyName;
}
// ... geter setter
@JsonAnyGetter
public Map<String, Object> any() {
return Collections.singletonMap(propertyName, data);
}
}
And use it like below:
Data<Difficulties> difficulties = new Data<>("difficulties");
write whatever you want instead of "difficulties" string. Set your list to Data generic class instead of Difficulties object if you want
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With