代码之家  ›  专栏  ›  技术社区  ›  Felk

使用类的字段中指定的自定义反序列化程序反序列化字符串

  •  4
  • Felk  · 技术社区  · 6 年前

    我需要写一个方法,采用一些对象,一些字段名 fieldName 存在于给定对象的类中,以及一些字段值 value

    static void setField(Object obj, String fieldName, String value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName)
        Object valObj = objectMapper.readValue(value, field.getType());
        field.set(obj, valObj);
    }
    

    (实际上,我只需要检索反序列化的值,而不需要再次设置它,但这使它成为一个更好的示例。) 只要jackson的默认反序列化是足够的,这就行了。现在假设我有一个带有自定义(反)序列化程序的类:

    class SomeDTO {
        String foo;
        @JsonSerialize(using = CustomInstantSerializer.class)
        @JsonDeserialize(using = CustomInstantDeserializer.class)
        Instant bar;
    }
    

    一种可能的解决方案是手动检查 JsonDeserialize 注释。但是,我真的不想尝试复制Jackson遵循的任何策略来决定使用哪个序列化程序,因为这看起来很脆弱(例如全局注册的序列化程序)。

    我设法找到了一个 AnnotatedMember 实例,其中包含所有必需的信息(JSON注释和反射字段或setter/getter访问),但由于缺少文档,无法确定如何使用它反序列化独立值:

    final JavaType dtoType = objectMapper.getTypeFactory().constructType(SomeDTO.class);
    final BeanDescription description = objectMapper.getDeserializationConfig().introspect(dtoType);
    for (BeanPropertyDefinition propDef: beanDescription.findProperties()) {
        final AnnotatedMember mutator = propertyDefinition.getNonConstructorMutator();
        // now what? Also: How do I filter for the correct property?
    }
    
    2 回复  |  直到 6 年前
        1
  •  1
  •   Felk    6 年前

    一种可能是序列化对象,替换给定字段,然后再次反序列化。这在从/到序列化时很容易做到 JsonNode

    static Object setField(Object obj, String fieldName, String value) throws Exception {
        // note: produces a new object instead of modifying the existing one
        JsonNode node = objectMapper.valueToTree(obj);
        ((ObjectNode) node).put(fieldName, value);
        return objectMapper.readValue(node.traverse(), obj.getClass());
    }
    

        2
  •  0
  •   Beastmaster    6 年前
    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.core.JsonParser;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.DeserializationContext;
    import com.fasterxml.jackson.databind.DeserializationFeature;
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializerProvider;
    import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
    import com.fasterxml.jackson.databind.annotation.JsonSerialize;
    import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
    import com.fasterxml.jackson.databind.ser.std.StdSerializer;
    
    import java.io.IOException;
    import java.util.Map;
    
    public final class Jackson {
    
      private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
          .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
    
      public static void main(String[] args) throws IOException {
        Dto source = makeDto("Master", 31337);
        Dto dst = makeDto("Slave", 0xDEADBEEF);
    
        //1. read value of field "fieldName" from json source
        //2. clones destination object, sets up field "fieldName" and returns it
        //3. in case of no field either on "src" or "dst" - throws an exception
        Object result = restoreValue(dst, "details", OBJECT_MAPPER.writeValueAsString(source));
        System.out.println(result);
      }
    
      private static Object restoreValue(Object targetObject, String fieldName, String sourceObjectAsJson) throws IOException {
        String targetObjectAsJson = OBJECT_MAPPER.writeValueAsString(targetObject);
        Map sourceAsMap = OBJECT_MAPPER.readValue(sourceObjectAsJson, Map.class);
        Map targetAsMap = OBJECT_MAPPER.readValue(targetObjectAsJson, Map.class);
        targetAsMap.put(fieldName, sourceAsMap.get(fieldName));
        String updatedTargetAsJson = OBJECT_MAPPER.writeValueAsString(targetAsMap);
        return OBJECT_MAPPER.readValue(updatedTargetAsJson, targetObject.getClass());
      }
    
      private static Dto makeDto(String name, int magic) {
        Dto dto = new Dto();
        dto.setName(name);
        CustomDetails details = new CustomDetails();
        details.setMagic(magic);
        dto.setDetails(details);
        return dto;
      }
    
      private static final class Dto {
        private String name;
        @JsonSerialize(using = CustomDetails.CustomDetailsSerializer.class)
        @JsonDeserialize(using = CustomDetails.CustomDetailsDeserializer.class)
        private CustomDetails details;
    
        public String getName() {
          return name;
        }
    
        public void setName(String name) {
          this.name = name;
        }
    
        public CustomDetails getDetails() {
          return details;
        }
    
        public void setDetails(CustomDetails details) {
          this.details = details;
        }
    
        @Override
        public String toString() {
          return "Dto{" +
              "name='" + name + '\'' +
              ", details=" + details +
              '}';
        }
      }
    
    
      private static final class CustomDetails {
        private int magic;
    
        public int getMagic() {
          return magic;
        }
    
        public void setMagic(int magic) {
          this.magic = magic;
        }
    
        @Override
        public String toString() {
          return "CustomDetails{" +
              "magic=" + magic +
              '}';
        }
    
        public static final class CustomDetailsSerializer extends StdSerializer<CustomDetails> {
    
          public CustomDetailsSerializer() {
            this(null);
          }
    
    
          public CustomDetailsSerializer(Class<CustomDetails> t) {
            super(t);
          }
    
          @Override
          public void serialize(CustomDetails details, JsonGenerator jg, SerializerProvider serializerProvider) throws IOException {
            jg.writeStartObject();
            jg.writeNumberField("_custom_property_magic", details.magic);
            jg.writeEndObject();
          }
        }
    
    
        private static final class CustomDetailsDeserializer extends StdDeserializer<CustomDetails> {
    
          public CustomDetailsDeserializer() {
            this(null);
          }
    
    
          public CustomDetailsDeserializer(Class<CustomDetails> t) {
            super(t);
          }
    
          @Override
          public CustomDetails deserialize(JsonParser jp, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
            JsonNode node = jp.getCodec().readTree(jp);
            int magic = (Integer) node.get("_custom_property_magic").numberValue();
            CustomDetails
                customDetails = new CustomDetails();
            customDetails.setMagic(magic);
            return customDetails;
          }
        }
      }
    }
    

    Dto{name='Slave', details=CustomDetails{magic=31337}}