Jackson Generics with variable JsonProperty (usage with generics)

后端 未结 2 1176
青春惊慌失措
青春惊慌失措 2020-12-18 14:48

I have class that looks like this:

public class Data {

    @JsonProperty(\"difficulties\")
    private U[] data;

    // ... geter setter construct         


        
相关标签:
2条回答
  • 2020-12-18 15:13

    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

    0 讨论(0)
  • 2020-12-18 15:38

    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

    0 讨论(0)
提交回复
热议问题