杰克逊是否将第二个字符反序列化为小写 属性

Does Jackson deserialise the second character to lowercase for a property

我们在服务代码中定义了一个模型为 -

@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public class SomeData {

    public boolean tnAvailable;

    @NonNull
    public String sTempChange;

    public boolean isTnAvailable() {
       return faAvailable;
    }

    public void setTnAvailable(boolean faAvailable) {
        this.faAvailable = faAvailable;
    }

    @Nonnull
    public String getSTempChange() {
        return sTempChange;
    }

    public void setSTempChange(@Nonnull String sTempChange) {
        this.sTempChange = sTempChange;
    }

}

当查询包含上述模型的api响应时,我们得到的响应为-

"someData": {
    "tnAvailable": true,
    "stempChange": "trial_001"
}

令我们惊讶的是 stempChange(注意小写 t)而不是响应属性中的 sTempChange

在对API 调用期间的对象,因为我们不使用任何其他 getter-setter 包装器更改属性。 为什么会这样,serialization/deserialization寻找这个的正确方向是什么?

Edit - 来自@Windle 的评论,试图解释这里的不同之处。我再次重复"The question though there relates pretty much to the same situation. Yet I 'm looking forward to the reason's for such implementation and documentation in fasterxml as well."

是的,看起来它对方法名称感到困惑。您可以使用@JsonGetter 注释强制序列化名称

@JsonGetter("sTempChange")
public String getSTempChange() {
    return sTempChange;
}

处理 getters/setters 中的多个前导大写字母(如 "getURL()" 或 "getFName()")。 默认情况下,Jackson 将简单地将所有前导大写字母小写,给出 "url" 和 "fname"。 但是如果你启用 MapperFeature.USE_STD_BEAN_NAMING(在 Jackson 2.5 中添加),它将遵循 Java Bean 命名约定,这只是小写一个大写-大小写首字母;如果找到多个,什么都不做。 这将导致属性 "URL" 和 "FName".

当我第一次试用您的 SomeData class 并对其进行序列化时,我得到了以下结果:

{"tnAvailable":true,"sTempChange":"trial_000","stempChange":"trial_000"}

这意味着 jackson 不匹配您的 getters/setters 与 sTempChange 属性,它们被视为不同的属性。在为我的映射器添加以下配置后,我能够重现您的案例:

    ObjectMapper objectMapper = new ObjectMapper();

    objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
    objectMapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.ANY);
    objectMapper.setVisibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.ANY);
    objectMapper.setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.ANY);

现在你的错误的原因是因为 Jackson 使用它自己的 bean 实用程序实现(com.fasterxml.jackson.databind.util.BeanUtil),当 class 被处理为字段,getters 和setters(由 com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector 完成)当实例为 serialized/deserialized 时。感兴趣的方法是okNameForGetterokNameForSetter。在这些方法中,根据 MapperFeature.USE_STD_BEAN_NAMING 还使用了 2 种其他方法(它在所有方法的 stdNaming 参数中传递)。两种方法的使用方式如下:

return stdNaming
                ? stdManglePropertyName(name, prefix.length())
                : legacyManglePropertyName(name, prefix.length());

stdManglePropertyName 遵循第 8.8 节中的 Java Beans 规范,而 legacyManglePropertyName 不遵循并且在 Jackson 2.5 之前的版本中使用。

现在在 运行 你的 getter 和 setter 方法名称之后,无论你如何设置 MapperFeature.USE_STD_BEAN_NAMING,你的 getter/setter因为 sTempChange 属性 被错误命名。它应该是 getsTempChange(小写 's')和 getsTempChange(再次小写 's')以正确序列化和反序列化 SomeData class 的实例。

最后是一些测试代码:

import com.fasterxml.jackson.databind.ObjectMapper;


public class Test {

static class SomeData {

    public boolean tnAvailable;

    public String sTempChange;

    public String getsTempChange() {
        return sTempChange;
    }

    public void setsTempChange(String sTempChange) {
        this.sTempChange = sTempChange;
    }

    public boolean isTnAvailable() {
        return tnAvailable;
    }

    public void setTnAvailable(boolean tnAvailable) {
        this.tnAvailable = tnAvailable;
    }

}

public static void main(String[] args) {

    ObjectMapper objectMapper = new ObjectMapper();

//  objectMapper.configure(MapperFeature.USE_STD_BEAN_NAMING, true);

    SomeData someData = new SomeData();
    someData.setsTempChange("trial_000");
    someData.setTnAvailable(true);

//  objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
//  objectMapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.ANY);
//  objectMapper.setVisibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.ANY);
//  objectMapper.setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.ANY);

    try {
        System.out.println("Serialize: " + objectMapper.writeValueAsString(someData));

        String json = "{ \"tnAvailable\": false, \"sTempChange\": \"trial_001\" }";

        SomeData anotherData = objectMapper.readValue(json, SomeData.class);

        System.out.println("Deserialize: " + anotherData.isTnAvailable() + ", " + anotherData.getsTempChange());

    } catch (Exception e) {
        e.printStackTrace();
    }

}

}