Jackson Generics with variable Json response object and Json property

Based on the answer ‘Jackson Generics with variable JsonProperty‘ by VizGhar, I am trying to use WebClient to consume an API response which contains a generic object in json response:

{
"meta": { 
    "RID": "abc9f-defgh-hj78k-lkm9n",
    "QID": "abc9f-defgh-hj78k-lkm9n" },
"data": { 
        "Inquiry": {
            "multiCurrency": [{"TaxStat": "Y", "TaxAmt": 0}],
            "Type": "Tax",
            "TaxFreq": { 
                "weekDay": 0,
                "startDay": 0 
            },
            "TaxRegion": "Tx" 
        } 
    }
}

Where type of “Inquiry” would be generic, i.e. “data” is a wrapper around a generic response object which is ‘Inquiry’ in this case, but it may change.

Inquiry.java:

public class Inquiry {
    @JsonProperty("multiCurrency")
    private List<MultiCurrencyInq> multiCurrency;

    @JsonProperty("Type")
    private String Type;

    @JsonProperty("TaxFreq")
    private TaxFreq taxFreq;

    @JsonProperty("TaxRegion")
    private String TaxRegion;

    // Getters Setters Constructors
}

MultiCurrencyInq.java:

public class MultiCurrencyInq {

    @JsonProperty("TaxStat")
    private String TaxStat;

    @JsonProperty("TaxAmt")
    private int TaxAmt;

    // Getters Setters Constructors
}

TaxFreq.java:

public class TaxFreq {

    @JsonProperty("weekDay")
    private int weekDay;

    @JsonProperty("startDay")
    private int startDay;

    // Getters Setters Constructors
}

My Response.java looks like this:

public class Response<T>{
    private Meta meta;
    private Data<T> data;
    // Getters Setters Constructors
}

Meta.java:

public class Meta{
    private String RID;
    private String QID;
    // Getters Setters Constructors
}

Data.java:

public class Data<T> {
    // property name, that will be changed
    @JsonProperty(DataNamingStrategy.DATA_FIELD)
    private T data;
    // Getters Setters Constructors
}

My Controller:

@RestController
public class InquiryController {

    @Autowired private WebClient webClient;

    @GetMapping("/inquiry") public Response<Inquiry> getInquiryApiResponse() {
        ResponseEntity<String> response = webClient.get()
                .uri("http://my.org.com/clientId/inquiry")
                .retrieve()
                .toEntity(String.class)
                .block();

        ObjectMapper mapper = new ObjectMapper();
        mapper.setPropertyNamingStrategy(new DataNamingStrategy("Inquiry")); 
        JavaType type = mapper.getTypeFactory()
                .constructParametricType(Response.class, Inquiry.class);

        Response<Inquiry> res = mapper.readValue(response.getBody(), type);
        return res;
    }
}

DataNamingStrategy.java:

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);
    }
}

This is not working for me. What could be the reason for not setting the generic type to “Inquiry” for @JsonProperty(DataNamingStrategy.DATA_FIELD) in Data.java

Answer

What could be the reason for not setting the generic type to “Inquiry” for @JsonProperty(DataNamingStrategy.DATA_FIELD) in Data.java

The reason is that by default JsonProperty annotated property name cannot be renamed by DataNamingStrategy. Jackson has this feature which is disabled by default.

ALLOW_EXPLICIT_PROPERTY_RENAMING Feature that when enabled will allow explicitly named properties (i.e., fields or methods annotated with JsonProperty(“explicitName”)) to be re-named by a PropertyNamingStrategy, if one is configured. Feature is disabled by default.

Since: 2.7

All you need to do is to enable this feature –

ObjectMapper mapper = new ObjectMapper();
mapper.enable(MapperFeature.ALLOW_EXPLICIT_PROPERTY_RENAMING);

Here is the related Jackson notes history (referred from here)

Prior versions allowed explicit property renaming by default
v2.4 - Jackson stopped allowing property renaming. (#428)
v2.7 - Introduced ALLOW_EXPLICIT_PROPERTY_RENAMING feature to allow / disallow Explicit Property renaming (#918)