springboot升级后@JsonProperty不起作用

问题描述

我最近将我的应用程序从 Spring Boot 1.5.3 升级到 Spring Boot 2.3.5。 UI 上的一项功能开始崩溃,我在调试时发现对 UI 的 json 响应发生了变化

回复

"modelExplanation": [{
                                "sectionId": 0,"sectionText": "some text","startIndex": "0","endindex": "105","modelExplanations": [{
                                                "modelId": "mdl_collusion","modelName": "collusion","modelVersion": "3.4.1","score": "1.0","riskStatus": "low"
                                },{
                                                "modelId": "mdl_mkt_mnpltn","modelName": "market_manipulation",{
                                                "modelId": "mdl_secrecy","modelName": "secrecy","riskStatus": "low"
                                }]
                }]
}

回复:在属性名称中有下划线 (_)

                                "section_id": 1,"section_text": "some text","start_index": "738","end_index": "1112","model_explanations": [{
                                                "model_id": "mdl_collusion","model_name": "collusion","model_version": "3.4.1","section_score": "0.09059832","risk_status": "low"
                                },{
                                                "model_id": "mdl_mkt_mnpltn","model_name": "market_manipulation","section_score": "0.12787165",{
                                                "model_id": "mdl_secrecy","model_name": "secrecy","section_score": "0.19208406","risk_status": "low"
                                }]
                }]
}

似乎@JsonProperty 不起作用。我的课程如下:

  public ModelExplanationResponse getModelExplanation(User user,ModelRequest request) {
    ModelExplanationMetadata Metadata = null;
    try {
        String modelExplanationjson = anotherService.getModelExplanation(user,request.getMessageId(),request.getSource(),request.getAvroId(),request.getRundate());
        
        if (modelExplanationjson != null && !"".equals(modelExplanationjson)) {
            Metadata = mapJSONToMetadata(modelExplanationjson);
        }
    } catch (Exception e) {
        throw new AlertMngtServiceException("Unable to retrieve attachment",e);
    }
    return getModelExplanationResponse(Metadata,request);
}

private ModelExplanationResponse getModelExplanationResponse(ModelExplanationMetadata Metadata,ModelRequest request) {
    ModelExplanationResponse response = new ModelExplanationResponse();
    if (Metadata != null && Metadata.getModelExplanation() != null) {
        List<ModelExplanationSection> modelExplanation = mapJSONToModelExplanation(Metadata.getModelExplanation());
        if (!CollectionUtils.isEmpty(modelExplanation)) {
            modelExplanation.sort(Comparator.comparing(ModelExplanationSection::getSectionId));
            response.setModelExplanation(modelExplanation);
            String formattedMessageBody = new ModelExplanationBasedMessageModifier().modify(request.getMessageBody(),modelExplanation);               
            response.setMessageBody(formattedMessageBody);
        }
    } else {
        LOGGER.info("Unable to fetch Model Explanation for {}",request);
    }
    return response;
}


import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnkNown = true)
public class ModelExplanationSection {

    private final int sectionId;
    private final String sectionText;
    private final String startIndex;
    private final String endindex;
    private final List<ModelExplanation> modelExplanations;

    @JsonCreator
    public ModelExplanationSection(
            @JsonProperty("section_id") int sectionId,@JsonProperty("section_text") String sectionText,@JsonProperty("start_index") String startIndex,@JsonProperty("end_index") String endindex,@JsonProperty("model_explanations") List<ModelExplanation> modelExplanations) {
        super();
        this.sectionId = sectionId;
        this.sectionText = sectionText;
        this.startIndex = startIndex;
        this.endindex = endindex;
        this.modelExplanations = modelExplanations;
    }

    public int getSectionId() {
        return sectionId;
    }

    public String getSectionText() {
        return sectionText;
    }

    public String getStartIndex() {
        return startIndex;
    }

    public String getEndindex() {
        return endindex;
    }

    public List<ModelExplanation> getModelExplanations() {
        return modelExplanations;
    }

}




import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Strings;

@JsonIgnoreProperties(ignoreUnkNown = true)
public class ModelExplanation {

    private final String modelId;
    private final String modelName;
    private final String modelVersion;
    private final String score;
    private final String riskStatus;

    @JsonCreator
    public ModelExplanation(
            @JsonProperty("model_id") String modelId,@JsonProperty("model_name") String modelName,@JsonProperty("model_version") String modelVersion,@JsonProperty("section_score") String score,@JsonProperty("risk_status") String riskStatus) {
        super();
        this.modelId = modelId;
        this.modelName = modelName;
        this.modelVersion = modelVersion;
        this.score = score;
        this.riskStatus = getRiskStatus(riskStatus);
    }

    private String getRiskStatus(String riskStatus) {
        return Strings.isNullOrEmpty(riskStatus)?"low":riskStatus;
    }

    public String getModelId() {
        return modelId;
    }

    public String getModelName() {
        return modelName;
    }

    public String getModelVersion() {
        return modelVersion;
    }

    public String getscore() {
        return score;
    }

    public String getRiskStatus() {
        return riskStatus;
    }

}

我试过@JsonCreator(mode = JsonCreator.Mode.PROPERTIES) 但它不起作用。 在 stack-overflow 上找到了另一个解决方案,建议将 jackson 注释导入替换为 org.codehaus.jackson.annotate,但这对我也不起作用。

这里使用相同的类有两个目的,1. @JsonProperty 用于将 json 响应从 anotherService 映射到 java 对象,然后使用相同的 java 对象将 java 对象序列化为 ui。我无法理解在升级之前它是如何工作的。

非常感谢任何帮助,因为这已经花了一天的时间,但我还无法识别和解决问题。

解决方法

我无法使用 Spring Boot 2.3.5 引入的 Jackson 2.11.3 来复制这个

以下测试使用camelCase正确序列化。

public class DualNamingStrategy {

    public static void main(String[] args) throws JsonProcessingException {
        final String json = """
                {
                  "model_id": "mdl_secrecy","model_name": "secrecy","model_version": "3.4.1","section_score": "0.19208406","risk_status": "low"
                }
                """;
        final ObjectMapper mapper = new ObjectMapper();
        final ModelExplanation explanation = mapper.readValue(json,ModelExplanation.class);
        final String serialised = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(explanation);
        System.out.println(serialised);
    }

}

输出:

{
  "modelId" : "mdl_secrecy","modelName" : "secrecy","modelVersion" : "3.4.1","score" : "0.19208406","riskStatus" : "low"
}

现在,这不是答案,所以我会在这里提供一个答案,尽管这是一种解决方法,因为问题似乎出在您发布的代码之外的其他地方。

您没有告诉我们您使用的是哪个版本的 Java,所以我假设它是 8 或更高版本,如果您使用的是 7 或更早版本,这将不起作用。

您可以删除所有 @JsonProperty 注释,添加 Jackson Java 8 Parameter Names module(以弥补删除的注释)并使用两个具有不同命名策略的 ObjectMapper: >

public class DifferentNamingStrategies {

    public static void main(String[] args) throws JsonProcessingException {
        final String json = "{\n" +
                            "  \"model_id\": \"mdl_secrecy\",\n" +
                            "  \"model_name\": \"secrecy\",\n" +
                            "  \"model_version\": \"3.4.1\",\n" +
                            "  \"section_score\": \"0.19208406\",\n" +
                            "  \"risk_status\": \"low\"\n" +
                            "}\n";

        // Default object mapper,this has camelCase just like the default in Spring
        final ObjectMapper camelMapper = new ObjectMapper()
                // This allows us to remove the @JsonProperty annotations from the constructor parameters
                .registerModule(new ParameterNamesModule());

        // Custom object mapper that we use to deserialise JSON with snake_case
        final ObjectMapper snakeMapper = new ObjectMapper()
                .registerModule(new ParameterNamesModule())
                .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);

        final ModelExplanation explanation = snakeMapper.readValue(json,ModelExplanation.class);
        final String serialised = camelMapper.writerWithDefaultPrettyPrinter().writeValueAsString(explanation);
        System.out.println(serialised);
    }

}