【问题标题】:Jackson-Serialiser: Ignore Field at Serialisation TimeJackson-Serialize:在反序列化时忽略字段
【发布时间】:2019-07-24 13:42:13
【问题描述】:

我的情况要求更复杂的序列化。我有一个班级Available(这是一个非常简化的sn-p):

public class Available<T> {
  private T value;
  private boolean available;
  ...
}

所以一个 POJO

class Tmp {
  private Available<Integer> myInt = Available.of(123);
  private Available<Integer> otherInt = Available.clean();
  ...
}

通常会导致

{"myInt":{available:true,value:123},"otherInt":{available:false,value:null}}

但是,我想要一个序列化程序来呈现相同的 POJO,如下所示:

{"myInt":123}

我现在拥有的:

public class AvailableSerializer extends JsonSerializer<Available<?>> {

  @Override
  public void serialize(Available<?> available, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException, JsonProcessingException {
    if (available != null && available.isAvailable()) {
      jsonGenerator.writeObject(available.getValue());
    }

    // MISSING: nothing at all should be rendered here for the field
  }

  @Override
  public Class<Available<?>> handledType() {
    @SuppressWarnings({ "unchecked", "rawtypes" })
    Class<Available<?>> clazz = (Class) Available.class;
    return clazz;
  }

}

测试

  @Test
  public void testSerialize() throws Exception {
    SimpleModule module = new SimpleModule().addSerializer(new AvailableSerializer());
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.registerModule(module);

    System.out.println(objectMapper.writeValueAsString(new Tmp()));
  }

输出

{"myInt":123,"otherInt"}

谁能告诉我如何做“失踪”的东西?或者如果我做错了,那我该怎么做呢?

我的限制是我不希望开发人员一直将@Json...-annotations 添加到Available 类型的字段中。所以上面的Tmp-class 是一个典型的 using 类应该是什么样子的例子。 如果可能的话......

【问题讨论】:

    标签: serialization jackson


    【解决方案1】:

    Include.NON_DEFAULT

    如果我们假设你的clean方法是这样实现的:

    class Available<T> {
    
        public static final Available<Object> EMPTY = clean();
    
        //....
    
        @SuppressWarnings("unchecked")
        static <T> Available<T> clean() {
            return (Available<T>) EMPTY;
        }
    }
    

    您可以将序列化包含设置为JsonInclude.Include.NON_DEFAULT 值,它应该跳过设置为EMPTY(默认)值的值。见下例:

    import com.fasterxml.jackson.annotation.JsonInclude;
    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.databind.JsonSerializer;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializerProvider;
    import com.fasterxml.jackson.databind.module.SimpleModule;
    import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
    
    import java.io.IOException;
    
    public class JsonApp {
    
        public static void main(String[] args) throws Exception {
            SimpleModule module = new SimpleModule();
            module.addSerializer(new AvailableSerializer());
    
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.registerModule(module);
            objectMapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);
    
            System.out.println(objectMapper.writeValueAsString(new Tmp()));
        }
    }
    
    class AvailableSerializer extends JsonSerializer<Available<?>> {
    
        @Override
        public void serialize(Available<?> value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException {
            jsonGenerator.writeObject(value.getValue());
        }
    
        @Override
        @SuppressWarnings({"unchecked", "rawtypes"})
        public Class<Available<?>> handledType() {
            return (Class) Available.class;
        }
    }
    

    上面的代码打印:

    {"myInt":123}
    

    自定义 BeanPropertyWriter

    如果您不想使用Include.NON_DEFAULT,您可以编写您的自定义BeanPropertyWriter 并跳过您想要的所有值。见下例:

    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.databind.BeanDescription;
    import com.fasterxml.jackson.databind.JsonSerializer;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializationConfig;
    import com.fasterxml.jackson.databind.SerializerProvider;
    import com.fasterxml.jackson.databind.module.SimpleModule;
    import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
    import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    public class JsonApp {
    
        public static void main(String[] args) throws Exception {
            SimpleModule module = new SimpleModule();
            module.addSerializer(new AvailableSerializer());
            module.setSerializerModifier(new BeanSerializerModifier() {
                @Override
                public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
                    List<BeanPropertyWriter> writers = new ArrayList<>(beanProperties.size());
    
                    for (BeanPropertyWriter writer : beanProperties) {
                        if (writer.getType().getRawClass() == Available.class) {
                            writer = new SkipNotAvailableBeanPropertyWriter(writer);
                        }
                        writers.add(writer);
                    }
    
                    return writers;
                }
            });
    
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.registerModule(module);
    
            System.out.println(objectMapper.writeValueAsString(new Tmp()));
        }
    }
    
    class AvailableSerializer extends JsonSerializer<Available<?>> {
    
        @Override
        public void serialize(Available<?> value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException {
            jsonGenerator.writeObject(value.getValue());
        }
    
        @Override
        @SuppressWarnings({"unchecked", "rawtypes"})
        public Class<Available<?>> handledType() {
            return (Class) Available.class;
        }
    }
    
    class SkipNotAvailableBeanPropertyWriter extends BeanPropertyWriter {
    
        SkipNotAvailableBeanPropertyWriter(BeanPropertyWriter base) {
            super(base);
        }
    
        @Override
        public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception {
            // copier from super.serializeAsField(bean, gen, prov);
            final Object value = (_accessorMethod == null) ? _field.get(bean) : _accessorMethod.invoke(bean, (Object[]) null);
            if (value == null || value instanceof Available && !((Available) value).isAvailable()) {
                return;
            }
    
            super.serializeAsField(bean, gen, prov);
        }
    }
    

    上面的代码打印:

    {"myInt":123}
    

    【讨论】:

    • 我不喜欢打电话给objectMapper.setSerializationInclusion(...),因为它会涵盖所有课程。幸运的是我可以做objectMapper.configOverride(Available.class).setInclude(JsonInclude.Value.construct(JsonInclude.Include.NON_DEFAULT, JsonInclude.Include.ALWAYS));,这似乎做同样的事情,但只适用于Available。所以目前看来我不需要BeanPropertyWriter
    【解决方案2】:

    Michał Ziober's answer 之后,我不得不寻找有关Include.NON_DEFAULT 和默认对象的信息,然后遇到this answer 解释Include.NON_EMPTY 在我的第一次研究中Google 没有返回(感谢Google)。

    所以事情变得更容易了,现在是:

    public class AvailableSerializer extends JsonSerializer<Available<?>> {
    
      @Override
      public void serialize(Available<?> available, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException, JsonProcessingException {
        jsonGenerator.writeObject(available.getValue());
      }
    
      @Override
      public Class<Available<?>> handledType() {
        @SuppressWarnings({ "unchecked", "rawtypes" })
        Class<Available<?>> clazz = (Class) Available.class;
        return clazz;
      }
    
      @Override
      public boolean isEmpty(SerializerProvider provider, Available<?> value) {
        return value == null || !value.isAvailable();
      }
    
    }
    

    通过测试

      @Test
      public void testSerialize() throws Exception {
        SimpleModule module = new SimpleModule().addSerializer(availableSerializer);
        objectMapper.registerModule(module);
        objectMapper.configOverride(Available.class).setInclude(
            // the call comes from JavaDoc of objectMapper.setSerializationInclusion(...)
            JsonInclude.Value.construct(JsonInclude.Include.NON_EMPTY, JsonInclude.Include.ALWAYS));
    
        Tmp tmp = new Tmp();
        assertThat(objectMapper.writeValueAsString(tmp)).isEqualTo("{\"myInt\":123}");
        tmp.otherInt.setValue(123);
        assertThat(objectMapper.writeValueAsString(tmp)).isEqualTo("{\"myInt\":123,\"otherInt\":123}");
      }
    

    所以,如果你赞成我的回答,请也赞成 Michał Ziober 的回答,因为这也是一种稍微不同的方法。

    【讨论】:

      猜你喜欢
      • 2018-07-21
      • 2016-07-26
      • 2020-02-16
      • 2012-02-01
      • 2017-12-13
      • 1970-01-01
      • 2012-06-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多