Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/365.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java、Jackson、带有扩展类的自定义反序列化程序_Java_Jackson_Deserialization_Jackson Databind - Fatal编程技术网

Java、Jackson、带有扩展类的自定义反序列化程序

Java、Jackson、带有扩展类的自定义反序列化程序,java,jackson,deserialization,jackson-databind,Java,Jackson,Deserialization,Jackson Databind,使用Jackson 2.10,我试图为基类编写一个自定义反序列化程序,但我必须反序列化具有未知字段名的字段。还有一个扩展类也可以扩展这个序列化程序 我曾尝试使用@AnyGetter和@AnySetter来完成它,它确实起了作用。现在我只是想知道是否有一种方法可以通过自定义反序列化程序来实现 我可以用一个基类来实现,但当某个类扩展它时,它会失败 这是我所做工作的样本。 下面是基类及其序列化程序,以及我在main中使用的方式 //基类 @JsonDeserialize(使用=BaseClassDes

使用Jackson 2.10,我试图为基类编写一个自定义反序列化程序,但我必须反序列化具有未知字段名的字段。还有一个扩展类也可以扩展这个序列化程序

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

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

这是我所做工作的样本。 下面是基类及其序列化程序,以及我在main中使用的方式

//基类
@JsonDeserialize(使用=BaseClassDeserializer.class)
公共静态类基类{
private ObjectNode customFields=JsonNodeFactory.instance.ObjectNode();
私有int-baseInt;
public int getBaseInt(){
返回baseInt;
}
公共void setBaseInt(int baseInt){
this.baseInt=baseInt;
}
公共JsonNode getCustomFields(){
返回自定义字段;
}
public void setCustomFields(ObjectNode customFields){
this.customFields=customFields;
}
public void putCustomFields(字符串键,JsonNode节点){
this.customFields.set(键,节点);
}
}
//BaseClasseDeserializer
公共静态类BaseClasseDeserializer扩展StdDeserializer{
公共BaseClasseDeserializer(){
这个(空);
}
公共BaseClasseDeserializer(类vc){
超级(vc);
}
@凌驾
公共基类反序列化(JsonParser JsonParser,反序列化上下文反序列化上下文)
抛出IOException、JsonProcessingException{
基类结果=新基类();
JsonNode节点=jsonParser.getCodec().readTree(jsonParser);
result.setBaseInt((整数)((IntNode)node.get(“baseInt”)).numberValue();
node.fieldNames();
迭代器迭代器=node.fieldNames();
while(iterator.hasNext()){
String fieldName=iterator.next();
如果(!“baseInt”.equals(fieldName)){
result.putCustomFields(fieldName,node.get(fieldName));
}
}
返回结果;
}
}
//main
公共静态void main(字符串[]args)引发JsonProcessingException{
字符串json=“{\n”
+\t\“baseInt\”:1\n
+“\t\“customObject\”:{\n”
+“\t\t\”键\“:\”值\“\n”
+\t}\n
+“\t\“customString\”:\“STRING\”,\n”
+“\t\”扩展字符串\“:\”字符串\“\n”
+ "}";
ObjectMapper mapper=新的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;
  }
}
这与一个
基类不能强制转换为ExtendedClass
异常


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

在反序列化器中,您总是返回类型为
BaseClass
的对象,并且不能将其强制转换为
ExtendedClass
。您需要在反序列化程序中实现类型识别功能。在您的情况下,返回的类型取决于属性
JSON
payload包含的内容。如果
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;
    }
}
以上代码打印:

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
    甚至
    Map
    。将
    POJO
    类与
    3方库绑定不是一个好主意
  • 如果手动处理反序列化,则不需要使用
    @JsonProperty
    注释

谢谢您的回答,但这难道不意味着基类的可扩展性不强吗?像所有将来扩展基类的类一样,我必须返回并相应地编辑它的反序列化程序。@Man KitYau,不幸的是,它看起来是这样的。如果您没有类型列表,并且无法使用
@JsonTypeInfo
@JsonSubTypes
,则需要实现自定义类型识别机制。在您的例子中,您可以通过检查
JSON
payload包含的属性来做到这一点。如果希望使其易于扩展,则需要创建
type
属性,该属性将通知
Jackson
您希望反序列化的类型。
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}