【问题标题】: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 上适用于大多数情况下的原始属性。

【问题讨论】:

    标签: java json jackson


    【解决方案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 告诉反序列化器在反序列化期间保留属性的旧值。
      猜你喜欢
      • 1970-01-01
      • 2021-12-18
      • 1970-01-01
      • 2017-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-17
      相关资源
      最近更新 更多