GenericJackson2JsonRedisSerializer Ignore Class and Attribute

和自甴很熟 提交于 2021-01-29 10:32:07

问题


I'm trying to serialize ClassA into JSON and then deserialize and map it into ClassB only on similar attribute, ignoring a lot of checking. Lets say I have

public class First{
   private String attribute1;
   private String attribute2;
   private String attribute3;
}

public class Second{
   private Int attribute1;
   private String attribute2;
   private String attribute4;
}

What am I expecting is mapping an object of Second like

{
   "attribute2": "value2"
}

Is it possible? I need to do this because I have a shared redis cache key among some microservices. There might be different definition of the class stored in redis due to design change and it's unviable to check and update every microservice's codes. Thank you


回答1:


There's no direct way to tell GenericJackson2JsonRedisSerializer to ignore some fields and cast a class A to B, you can implement whatever strategy you want for deserialization.

A simple example could be, you register your mapping and ignorable fields when you want to do type conversion.

// Adapted from spring data redis
public class RqueueRedisSerDes implements RedisSerializer<Object> {
    private ObjectMapper mapper;

    @AllArgsConstructor
    @Getter
    class Dataum {
      Class<?> tgtClass;
      String[] ignorableProperties;
    }

    private Map<Class<?>, Dataum> classMap = new ConcurrentHashMap<>();

    RqueueRedisSerDes() {
      this.mapper = new ObjectMapper();
      this.mapper =
          mapper.registerModule(new SimpleModule().addSerializer(new NullValueSerializer()));
      this.mapper = mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
      this.mapper = mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);
    }

    public void addClassMap(Class<?> source, Class<?> tgt, String[] ignorableProperties) {
      classMap.put(source, new Dataum(tgt, ignorableProperties));
    }

    @Override
    public byte[] serialize(Object source) throws SerializationException {
      if (source == null) {
        return SerializationUtils.EMPTY_ARRAY;
      }
      try {
        return mapper.writeValueAsBytes(source);
      } catch (JsonProcessingException e) {
        throw new SerializationException("Could not write JSON: " + e.getMessage(), e);
      }
    }

    @Override
    public Object deserialize(byte[] source) throws SerializationException {
      if (SerializationUtils.isEmpty(source)) {
        return null;
      }
      try {
        Object object = mapper.readValue(source, Object.class);
        for (Entry<Class<?>, Dataum> entry : classMap.entrySet()) {
          if (ClassUtils.isAssignable(entry.getKey(), object.getClass())) {
            Dataum dataum = entry.getValue();
            Object tgt = dataum.getTgtClass().newInstance();
            BeanUtils.copyProperties(object, tgt, dataum.getIgnorableProperties());
            return tgt;
          }
        }
        return object;
      } catch (Exception ex) {
        throw new SerializationException("Could not read JSON: " + ex.getMessage(), ex);
      }
    }

    private static class NullValueSerializer extends StdSerializer<NullValue> {

      private static final long serialVersionUID = 211020517180777825L;
      private final String classIdentifier;

      NullValueSerializer() {
        super(NullValue.class);
        this.classIdentifier = "@class";
      }

      @Override
      public void serialize(
          NullValue value, JsonGenerator jsonGenerator, SerializerProvider provider)
          throws IOException {
        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField(classIdentifier, NullValue.class.getName());
        jsonGenerator.writeEndObject();
      }
    }
  }

Define a class that would implement RedisSerializer<Object> use this class in RedisConnectionFactory to serialize/deserialize values.

class SerializerTest{      
  @Data
  @AllArgsConstructor
  @NoArgsConstructor
  public static class First {
    private String attribute1;
    private String attribute2;
    private String attribute3;
  }
  @Data
  @ToString
  public static class Second {
    private Integer attribute1;
    private String attribute2;
    private String attribute4;
  }

  public static void main(String[] args) {
    RqueueRedisSerDes serDes = new RqueueRedisSerDes();
    // ignore attribute1 due to different type
    serDes.addClassMap(First.class, Second.class, new String[]{"attribute1"});
    First first = new First("1", "2", "3");
    byte[] out = serDes.serialize(first);
    Second second = (Second) serDes.deserialize(out);
    System.out.println(second);
  }
}

I've just modified code from my Repo Rqueue



来源:https://stackoverflow.com/questions/65612342/genericjackson2jsonredisserializer-ignore-class-and-attribute

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!