【问题标题】:Java, Jackson, Custom deserializer with extended classesJava,Jackson,具有扩展类的自定义反序列化器
【发布时间】:2019-08-15 02:21:45
【问题描述】:

使用 Jackson 2.10.,我正在尝试为基类编写自定义反序列化程序,但我必须反序列化字段名称未知的字段。然后是扩展类,也可以扩展这个序列化器。

我尝试使用@AnyGetter 和@AnySetter 来完成它,它确实有效。现在我只是想知道是否有办法通过自定义反序列化器来做到这一点。

我可以用一个基类来做,但是当某个类扩展它时它会失败。

这是我所做的示例。 以下只是基类及其序列化器以及我在 main 中的使用方式。

//BaseClass
@JsonDeserialize(using = BaseClassDeserializer.class)
public static class BaseClass {
  private ObjectNode customFields = JsonNodeFactory.instance.objectNode();
  private int baseInt;

  public int getBaseInt() {
    return baseInt;
  }

  public void setBaseInt(int baseInt) {
    this.baseInt = baseInt;
  }

  public JsonNode getCustomFields() {
    return customFields;
  }

  public void setCustomFields(ObjectNode customFields) {
    this.customFields = customFields;
  }

  public void putCustomFields(String key, JsonNode node) {
    this.customFields.set(key, node);
  }
}
// BaseClassDeserializer
public static class BaseClassDeserializer extends StdDeserializer<BaseClass> {
  public BaseClassDeserializer() {
    this(null);
  }

  public BaseClassDeserializer(Class<?> vc) {
    super(vc);
  }

  @Override
  public BaseClass deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
      throws IOException, JsonProcessingException {
    BaseClass result = new BaseClass();
    JsonNode node = jsonParser.getCodec().readTree(jsonParser);
    result.setBaseInt((Integer) ((IntNode) node.get("baseInt")).numberValue());
    node.fieldNames();
    Iterator<String> iterator = node.fieldNames();
    while (iterator.hasNext()) {
      String fieldName = iterator.next();
      if (!"baseInt".equals(fieldName)) {
        result.putCustomFields(fieldName, node.get(fieldName));
      }
    }
    return result;
  }
}
// main
public static void main(String[] args) throws JsonProcessingException {
  String json = "{\n"
      + "\t\"baseInt\": 1,\n"
      + "\t\"customObject\" : {\n"
      + "\t\t\"key\": \"value\"\n"
      + "\t},\n"
      + "\t\"customString\" : \"STRING\",\n"
      + "\t\"extendedString\" : \"STRING\"\n"
      + "}";

  ObjectMapper mapper = new ObjectMapper();
  BaseClass myClass = mapper.readValue(json, BaseClass.class);

}

通过查看调试器,字段已成功加载。

现在我正在尝试扩展BaseClass

// ExtendedClass
public static class ExtendedClass extends BaseClass {
  @JsonProperty("extendedString")
  private String extendedString;

  public String getExtendedString() {
    return extendedString;
  }

  public void setExtendedString(String extendedString) {
    this.extendedString = extendedString;
  }
}
public static void main(String[] args) throws JsonProcessingException {
  String json = "{\n"
      + "\t\"baseInt\": 1,\n"
      + "\t\"customObject\" : {\n"
      + "\t\t\"key\": \"value\"\n"
      + "\t},\n"
      + "\t\"customString\" : \"STRING\",\n"
      + "\t\"extendedString\" : \"STRING\"\n"
      + "}";

  ObjectMapper mapper = new ObjectMapper();
  ExtendedClass myClass = mapper.readValue(json, ExtendedClass.class);

}

这会导致崩溃 BaseClass cannot be cast to ExtendedClass 异常。

我猜我必须将反序列化传递给子类的反序列化器,但我不知道如何。

【问题讨论】:

    标签: java jackson deserialization jackson-databind


    【解决方案1】:

    在您的反序列化器中,您总是返回BaseClass 类型的对象,并且它不能转换为ExtendedClass。您需要在反序列化器中实现类型识别功能。在您的情况下,返回的类型取决于 JSON 有效负载包含的属性。如果JSON 有效负载包含extendedString 属性,您知道您需要返回ExtendedClass,否则只需返回BaseClass。您的反序列化器可能如下所示:

    class BaseClassDeserializer extends StdDeserializer<BaseClass> {
    
        public BaseClassDeserializer() {
            super(BaseClass.class);
        }
    
        @Override
        public BaseClass deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
            ObjectNode root = jsonParser.readValueAsTree();
    
            List<String> names = getNames(root);
            BaseClass result = findAndInitCustomType(names, root);
            result = initBase(names, result, root);
            initCustomFields(names, root, result);
    
            return result;
        }
    
        private void initCustomFields(List<String> names, ObjectNode root, BaseClass result) {
            for (String name : names) {
                result.putCustomFields(name, root.get(name));
            }
        }
    
        private BaseClass findAndInitCustomType(List<String> names, ObjectNode root) {
            final String extendedString = "extendedString";
            if (names.contains(extendedString)) {
                ExtendedClass result = new ExtendedClass();
                result.setExtendedString(root.get(extendedString).asText());
                names.remove(extendedString);
                return result;
            }
            // else - check other custom fields for another types.
            // if not available return null
            return null;
        }
    
        private BaseClass initBase(List<String> names, BaseClass baseClass, ObjectNode root) {
            if (baseClass == null) {
                baseClass = new BaseClass();
            }
            final String baseInt = "baseInt";
            if (names.contains(baseInt)) {
                baseClass.setBaseInt(root.get(baseInt).asInt());
                names.remove(baseInt);
            }
    
            return baseClass;
        }
    
        private List<String> getNames(ObjectNode root) {
            List<String> names = new ArrayList<>();
            root.fieldNames().forEachRemaining(names::add);
    
            return names;
        }
    }
    

    示例用法:

    import com.fasterxml.jackson.annotation.JsonProperty;
    import com.fasterxml.jackson.core.JsonParser;
    import com.fasterxml.jackson.databind.DeserializationContext;
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
    import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
    import com.fasterxml.jackson.databind.node.JsonNodeFactory;
    import com.fasterxml.jackson.databind.node.ObjectNode;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    public class JsonApp {
    
        public static void main(String[] args) throws Exception {
            String baseJson = "{"
                    + "\"baseInt\": 1,\n"
                    + "\t\"customObject\" : {\n"
                    + "\t\t\"key\": \"value\"\n"
                    + "\t},\n"
                    + "\t\"customString\" : \"STRING\""
                    + "}";
    
            String extendedJson = "{"
                    + "\t\"baseInt\": 1,\n"
                    + "\t\"customObject\" : {\n"
                    + "\t\t\"key\": \"value\"\n"
                    + "\t},\n"
                    + "\t\"customString\" : \"STRING\",\n"
                    + "\t\"extendedString\" : \"STRING\"\n"
                    + "}";
    
            ObjectMapper mapper = new ObjectMapper();
            System.out.println(mapper.readValue(baseJson, BaseClass.class));
            System.out.println(mapper.readValue(extendedJson, BaseClass.class));
            System.out.println(mapper.readValue(extendedJson, ExtendedClass.class));
        }
    }
    

    上面的代码打印:

    BaseClass{customFields={"customObject":{"key":"value"},"customString":"STRING"}, baseInt=1}
    ExtendedClass{extendedString='STRING'} BaseClass{customFields={"customObject":{"key":"value"},"customString":"STRING"}, baseInt=1}
    ExtendedClass{extendedString='STRING'} BaseClass{customFields={"customObject":{"key":"value"},"customString":"STRING"}, baseInt=1}
    

    改进:

    • BaseClass 类中而不是ObjectNode 使用Map&lt;String, JsonNode&gt; 甚至Map&lt;String, Object&gt;。将POJO 类与3-rd party libraries 绑定不是一个好主意。
    • 如果手动处理反序列化,则不需要使用@JsonProperty 注解。

    【讨论】:

    • 感谢您的回答,但这不是使 BaseClass 的可扩展性不强吗?就像所有未来扩展 BaseClass 的类一样,我将不得不返回并相应地编辑反序列化器。
    • @Man-KitYau,不幸的是它看起来像这样。如果您没有类型列表并且无法使用@JsonTypeInfo@JsonSubTypesother annotations,则需要实现自定义类型识别机制。在您的情况下,您可以通过检查 JSON 有效负载包含的属性来做到这一点。如果您想让它易于扩展,您需要创建 type 属性,它将通知 Jackson 您希望反序列化的类型。
    猜你喜欢
    • 2018-07-15
    • 2016-02-13
    • 2011-04-12
    • 2023-04-10
    • 2016-01-29
    • 1970-01-01
    • 2022-11-23
    • 2013-12-30
    • 2020-05-28
    相关资源
    最近更新 更多