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

时间:2017-03-31 16:07:24

标签: java json jackson deserialization fasterxml

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

@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在响应的属性中。

怀疑原因是Jackson com.fasterxml.jackson.core:jackson-core: 2.5.2 ,同时在API调用期间对对象进行序列化和反序列化,因为我们不要使用任何其他getter-setter ot包装器来更改属性。 为什么会发生这种情况,并且序列化/反序列化是正确的方向来寻找它?

编辑 - 来自@Windle的评论,尝试解释这里有什么不同。我重新迭代“这个问题虽然在很大程度上与相同的情况有关。但我也很期待在fastxml中实现这种实现和文档的原因。”

3 个答案:

答案 0 :(得分:4)

在getter / setter中处理多个首字母大写字母(例如“getURL()”或“getFName()”)。 默认情况下,杰克逊将简单地小写所有前导大写字母,给出“url”和“fname”。 但是,如果您启用 MapperFeature.USE_STD_BEAN_NAMING (在Jackson 2.5中添加),它将遵循Java Bean命名约定将执行的操作,这只是小写的单个大写前导字母;如果发现多个,什么也不做。 这将导致属性“URL”和“FName”。

答案 1 :(得分:3)

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

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

答案 2 :(得分:1)

当我第一次尝试你的SomeData课程并将其序列化时,我得到了以下结果:

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

这意味着jackson不会将您的getter / setter与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),当为字段,getter和setter(由com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector完成)处理类时使用它们。一个实例被序列化/反序列化。感兴趣的方法是okNameForGetterokNameForSetter。在这些方法中,根据MapperFeature.USE_STD_BEAN_NAMING使用了另外两种方法(在所有方法中都在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,都会错误地命名sTempChange属性的getter / setter。应该getsTempChange(小写' s')和getsTempChange(再次小写' s')来正确序列化和反序列化SomeData的实例类。

最后,这里有一些测试代码:

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

}

}