反序列化时忽略属性

58

我有一个简单的接口,它具有用于获取和设置属性的方法。

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我们打算跟踪此属性,但是当将money属性发送到服务器并且Jackson尝试反序列化时,应用程序似乎会崩溃,抛出MalformedJsonException: 无法构造Money类型的对象。

最奇怪的是,在大多数情况下将@JsonIgnore放在setter上并在getter上放置@JsonProperty可以正常工作,当属性是原始类型时更是如此。
5个回答

76

3
这个解决方案更清洁。 - Lucas Kuhlemann
2
这也适用于不可变的 Kotlin 数据类。 - HeikoG
1
这是使用 Lombok 时唯一有效的解决方案,因为我没有用于放置注释的 getter/setter 代码(就像被接受的答案那样)。 - Somnium
1
@Somnium 很高兴你喜欢我的回答。需要注意的是,如果你定义了自己的 getters/setters,Lombok 将不会生成它自己的。这样可以让你为某些字段编写 getters/setters,使用注解装饰这些方法,然后让 Lombok 生成其他的 getters/setters。 - kmek
1
非常感谢...我会再试一次...@kmek :=) - Beezer
显示剩余8条评论

34

好的,所以@JsonIgnore的行为从1.9版本开始发生了根本性的改变(我认为是变得更糟了)。不要深入探讨为什么你的属性在反序列化期间没有被忽略的魔鬼细节,尝试使用以下代码来修复它:

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等。

1
这实际上是解决方案的一部分。我还不得不添加objectMapper.disable(MapperFeature.USE_GETTERS_AS_SETTERS),然后它就可以工作了。请编辑您的答案,我会接受它。很遗憾没有更简洁的方法来解决这个问题(至少我找不到)。 - Nishant Nagwani
@NishantNagwani - 奇怪,我在没有启用该功能的情况下就可以正常工作。我会在我的答案中添加一个可选注释,但我很好奇你的设置是否有所不同,需要使用“USE_GETTERS_AS_SETTERS”。 - Perception
我又试了一遍,但在我的环境中仍然无法运行。我正在使用jackson 2.1.1版本。以下是我收到的错误信息:{ "message": "传递给服务器的JSON格式不正确,数据类型错误:\n反序列化'setterless'属性'money'时出现问题:get方法返回null(通过引用链:com.abc.def.Money)" } - Nishant Nagwani
请加入我的聊天室 - Perception

12

使用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版本中进行了更新,以按上述详细信息工作。我认为JsonProperty的READ_ONLY访问对解决此问题没有任何作用。 - kmek
如果字段具有默认值,则在反序列化过程中不会更改它。即使JSON包含其新值。 使用READ_ONLY注释的JsonProperty告诉反序列化器在反序列化期间保留属性的旧值。 - Sergey Nemchinov

6

如果您不拥有或无法通过添加@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);

5

显然我的解决方案有些晚,但肯定对其他人有帮助。

背景: 在我的项目中,有一个类可以将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的描述(检测到的属性、getter、setter等)。 - Aleksander Stelmaczonek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接