Java,Jackson,具有扩展类的自定义反序列化器

时间:2019-08-15 02:21:45

标签: java jackson deserialization jackson-databind

使用Jackson 2.10。,我试图为基类编写一个自定义反序列化器,但是我必须对具有未知字段名称的字段进行反序列化。然后是扩展类,也可以扩展此序列化器。

我尝试使用@AnyGetter和@AnySetter来完成它,并且确实可以工作。现在我只是想知道是否有一种方法可以通过自定义反序列化器完成。

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

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

//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例外。

我猜想我必须将反序列化传递给子类的反序列化器,但我不知道怎么做。

1 个答案:

答案 0 :(得分:0)

在反序列化器中,您总是返回类型为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<String, JsonNode>甚至是Map<String, Object>。将POJO类与3-rd party libraries绑定不是一个好主意。
  • 如果您手动处理反序列化,则无需使用@JsonProperty注释。