杰克逊是否将第二个字符反序列化为小写 属性
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 时。感兴趣的方法是okNameForGetter
和okNameForSetter
。在这些方法中,根据 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();
}
}
}
我们在服务代码中定义了一个模型为 -
@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 时。感兴趣的方法是okNameForGetter
和okNameForSetter
。在这些方法中,根据 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();
}
}
}