【问题标题】:Jackson's BeanDeserializerModifier does not work with final fields杰克逊的 BeanDeserializerModifier 不适用于最终字段
【发布时间】:2019-01-08 06:24:44
【问题描述】:

我写了一个自定义序列化器和自定义反序列化器来序列化带有@Confidential 注解的属性。

@Data
public class Person {
   private String name;

   @Confidential
   private String address;
}

Custom Serializer 使用以下值序列化 POJO:

{ "name": "John Doe", "address": "Kearney St"}

如下:

{"name":"John Doe", "address": {"value":"IjIwMzEwIDU4dGggTG4gTkUi"}}

Custom Deserializer 还能够将 JSON 反序列化回 Person POJO。

但是,当我将 Person POJO 中的字段设置为最终字段时,序列化继续工作,但反序列化失败。

@Data
public class Person {
    private final String name;

    @Confidential
    private final String address;
 }

这是 BeanSerializerModifier 的实现:

@AllArgsConstructor
public class CustomDeserializerModifier extends BeanDeserializerModifier {
    private final ObjectMapper objectMapper;

    @Override
    public BeanDeserializerBuilder updateBuilder(final DeserializationConfig config,
                                                 final BeanDescription beanDesc,
                                                 final BeanDeserializerBuilder builder) {
        Iterator<SettableBeanProperty> beanPropertyIterator = builder.getProperties();
        beanPropertyIterator.forEachRemaining(settableBeanProperty -> {
            final Confidential annotation = settableBeanProperty.getAnnotation(Confidential.class);
            if (encryptedProperty != null) {
                JsonDeserializer<Object> current = settableBeanProperty.getValueDeserializer();
                final SettableBeanProperty newSettableBeanProperty =
                        settableBeanProperty.withValueDeserializer(
                                new CustomDeserializer(annotation, current, objectMapper)
                        );
                builder.addOrReplaceProperty(newSettableBeanProperty, true);
            }
        });

        return builder;
    }
}

我发现 CustomDeserializer 在 Person POJO 字段为 final 时永远不会被调用。

这是错误信息:

com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: {"name":"John Doe","address":{"value":"IjIwMzEwIDU4dGggTG4gTkUi"}}; line: 1, column: 30] (through reference chain: com.custom.model.Person["address"])

Jackson 专家能否告诉我为什么我的 CustomDeserializer 在 POJO 字段为最终字段时没有被调用。

谢谢!

【问题讨论】:

  • 只是一个快速的猜测:问题可能是由于缺少默认构造函数。您可以尝试将@NoArgsConstructor 添加到Person 吗?
  • 对不起,这不是一个选项,Jan。我们不能使类可变。所有字段都必须是最终字段。
  • 这不是解决方案建议,只是让您更清楚问题所在的步骤。
  • 如果您使用 Lombok >= 1.16.20,您也可以尝试将 lombok.anyConstructor.addConstructorProperties = true 添加到您的 lombok.config。
  • 没有杰克逊专家回答 2 年前提出的问题?我面临同样的问题,很想知道这个问题是否有解决方案?通过使用 bean 修饰符或其他方式。无论有没有龙目岛,问题都是一样的。任何帮助或反馈都非常受欢迎。

标签: java json jackson annotations lombok


【解决方案1】:

如前所述,序列化对可变字段和不可变字段都适用。只有在使用不可变字段时才会出现反序列化问题,因为 BeanDeserializerModifier 在这种情况下不起作用。

在杰克逊的术语中,不可变字段被命名为创建者属性,这意味着它们是使用创建者进行初始化的。请参阅 BeanDeserializerBase#resolve。

为了正确处理这个用例,可以使用自定义 DeserializationContext 创建 ObjectMapper(ObjectMapper 的扩展实现也可以设置与反序列化上下文相关的受保护字段)。

然后,通过重写 DeserializationContext#handleSecondaryContextualization 方法,可以更改反序列化以使其工作。

也许还有其他可能性,但这个在加密方面工作得很好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-10-08
    • 1970-01-01
    • 2015-08-01
    • 1970-01-01
    • 2015-05-18
    • 1970-01-01
    • 2021-12-02
    • 2013-08-16
    相关资源
    最近更新 更多