反序列化时忽略属性

Posted

技术标签:

【中文标题】反序列化时忽略属性【英文标题】:Ignoring property when deserializing 【发布时间】:2013-04-07 20:12:41 【问题描述】:

我有一个简单的接口,其中包含属性的 getter 和 setter。

public interface HasMoney  

      Money getMoney();

      void setMoney(Money money);

 

我有另一个类 UserAccount 实现了这个接口。

public class UserAccount implements HasMoney 

       private Money money;

       @Override
       Money getMoney() // fill in the blanks

       @Override
       void setMoney(Money money) // fill in the blanks


我的问题是我想序列化货币属性,但在反序列化时忽略它,即不接受用户为这个属性提供的任何值。我已经在 setter 上尝试了 @JsonIgnore ,在 getter 上尝试了 @JsonIgnore(false) ,它确实忽略了它,但它在序列化它的同时也这样做了。

我在 setter 上尝试了@JsonIgnore,在 getter 上尝试了@JsonProperty,只是为了明确告诉 Jackson 我们打算跟踪这个属性,当金钱属性被发送到服务器并且 Jackson 试图反序列化它抛出时,这似乎会使应用程序崩溃up MalformedJsonException : 无法构造 Money 类型的对象。

最奇怪的是,将@JsonIgnore 放在setter 上,将@JsonProperty 放在setter 上适用于大多数情况下的原始属性。

【问题讨论】:

【参考方案1】:

版本 2.6.0+ 允许在类级别使用 @JsonIgnoreProperties 完成此操作。

@JsonIgnoreProperties(value= "money" , allowGetters=true)

看看这个已关闭的问题: https://github.com/FasterXML/jackson-databind/issues/95

【讨论】:

这个解决方案更干净 这也适用于不可变的 kotlin 数据类。 这是唯一可行的解​​决方案,当使用 Lombok 时,因为那时我没有用于 getter/setter 的代码来添加注释(如接受的答案)。 @Somnium 很高兴您喜欢我的回答。请注意,如果您定义自己的 getter/setter,Lombok 将不会生成自己的。这允许您为某些字段编写 getter/setter,用注释装饰这些方法,然后让 Lombok 生成其他 getter/setter。 非常感谢...我会再试一次...@kmek :=)【参考方案2】:

好的,从 1.9 开始,@JsonIgnore 的行为发生了根本性的变化(更糟糕的是 imo)。无需详细说明为什么您的属性在反序列化期间没有被忽略,请尝试以下代码来修复它:

public class UserAccount implements HasMoney 
    @JsonIgnore
    private BigDecimal money;

    // Other variable declarations, constructors

    @Override
    @JsonProperty
    public BigDecimal getMoney() 
        return money;
    

    @JsonIgnore
    @Override
    public void setMoney(final BigDecimal money) 
        this.money = money;
    

    // Other getters/setters

注意在现场使用@JsonIgnore - 它是有效解决方案所必需的。

注意:根据您的环境和用例,您可能需要对 ObjectMapper 实例进行额外配置,例如, USE_GETTERS_AS_SETTERS、AUTO_DETECT_GETTERS、AUTO_DETECT_SETTERS

【讨论】:

这实际上是解决方案的一部分。我还必须添加 objectMapper.disable(MapperFeature.USE_GETTERS_AS_SETTERS) 然后它就起作用了。请编辑您的答案,我会接受。遗憾的是没有更清洁的方法(至少我找不到它)。 @NishantNagwani - 奇怪,在没有启用该功能的情况下为我工作。我正在为我的答案添加一个可选注释,但我很好奇您的设置是否有不同之处需要USE_GETTERS_AS_SETTERS 我又试了一次。不适用于我的环境。我正在使用杰克逊 2.1.1 。这是我得到的错误“消息”:“传递给服务器的格式错误,使用的数据类型不正确:\n反序列化'setterless'属性'money'的问题:get方法返回null(通过引用链:com.abc.def.Money ])" 请加入我的chat。【参考方案3】:

如果您不拥有或无法通过添加 @JsonIgnore 注释来更改类,您将在您的实现中使用从版本 2.5 开始的 mixin 来获得预期的结果。

public abstract class HasMoneyMixin 
    @JsonIgnore
    public abstract Money getMoney();

配置映射器使用mixin,

ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(HasMoney.class, HasMoneyMixin.class);
// avoid failing if all properties are empty
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

【讨论】:

【参考方案4】:

显然我的解决方案迟了,但肯定会对其他人有所帮助。

史前史: 在我的项目中,有一个类将 JSON 字符串直接读取到实体中。 JSON 包含一个不是类变量的属性。因为

objectMapper.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

在反序列化期间不会创建实体的实例(在我们的项目中需要这种情况下的异常)。

解决方案

objectMapper.addHandler(new DeserializationProblemHandler() 
    @Override
    public boolean handleUnknownProperty(DeserializationContext ctxt, JsonParser p, JsonDeserializer<?> deserializer, Object beanOrClass, String propertyName) throws IOException 
       if( (propertyName.equals("propertyToBeIgnored") && beanOrClass.getClass().equals(ClassOfTheProperty.class)) 
           p.skipChildren();
           return true;
        else 
           return false;
       
    
);

【讨论】:

使用ctxt.getConfig().introspect(ctxt.constructType(beanOrClass.getClass())) 可以获得bean 的描述(检测到的props、getter、setter 等)。【参考方案5】:

使用 Jackson 2.10,您可以像这样实现只读字段:

一个真实的领域

public class UserAccount implements HasMoney 

   @JsonProperty(access = JsonProperty.Access.READ_ONLY)
   private Money money;
   
   // getter and setter


虚拟字段

@JsonIgnoreProperties(ignoreUnknown = true) // to ignore ALL unknown properties
// OR
@JsonIgnoreProperties(value = "money", allowGetters = true) // to ignore only 'money' input
public class UserAccount implements HasMoney 

   @JsonProperty
   public Money getMoney() 
       // some calculation
   


该值将被序列化,但在反序列化过程中被忽略。

【讨论】:

这与我的回答有何不同?关键是 JsonIgnoreProperties,它已在 2.6.0 版中更新,可以按照上面详述的方式工作。我不认为具有 READ_ONLY 访问权限的 JsonProperty 对此问题没有任何作用。 如果字段有默认值,反序列化时不会改变。即使 JSON 包含它的新值。带有 READ_ONLY 的注释 JsonProperty 告诉反序列化器在反序列化期间保留属性的旧值。

以上是关于反序列化时忽略属性的主要内容,如果未能解决你的问题,请参考以下文章

我可以让 XmlSerializer 在反序列化时忽略命名空间吗?

使用带有 ItemRequired = Required.Always 的 Json.Net 反序列化时忽略属性

在 xml 序列化期间忽略属性,但在反序列化期间不忽略

在使用 Jackson 反序列化期间选择性地忽略 JSON 属性

XML 反序列化忽略不按字母顺序排列的属性

在 Java 中的 Jackson JSON 反序列化期间忽略丢失的属性