Java 如何将JSON字符串反序列化为不同的复杂类类型?

Java 如何将JSON字符串反序列化为不同的复杂类类型?,java,json,jackson,Java,Json,Jackson,我有一个JSONNode对象,它可以包含任何JSON内容。例如: { "fieldA": "aStringValue", "fieldB": 10, "fieldC": { "TypeAFieldA": "aValue" }, "fieldD": { "TypeBFieldA": "aValue", "TypeBFieldB": { "TypeCFieldA": "aValue",

我有一个JSONNode对象,它可以包含任何JSON内容。例如:

{
    "fieldA": "aStringValue",
    "fieldB": 10,
    "fieldC": {
        "TypeAFieldA": "aValue"
    },
    "fieldD": {
        "TypeBFieldA": "aValue",
        "TypeBFieldB": {
            "TypeCFieldA": "aValue",
            "TypeCFieldB": "bValue"
        }
    }
}
我想将此字符串中的每个JSON字段反序列化为不同类型的java对象,如下所示:

fieldA -> String object
fieldB -> int 
fieldC -> TypeA object
fieldD -> TypeB object
假设我知道每个字段应该反序列化到的类类型。 实现这一目标的最佳方式是什么

编辑:进一步澄清我的要求:

我想到的方法是为TypeA、TypeB、TypeC等创建对象,并用相关的JSONProperty注释对它们进行注释。 我不清楚的是,我如何逐个反序列化每个字段?为此,我需要从JsonNode中逐个提取json字符串,并使用相关的类类型运行对象映射器

示例:要将“fieldC”及其值反序列化为一个TypeC类,我是否需要执行以下操作:

  • 提取完整的Json字符串:

    字符串jsonString=“fieldC”:{“TypeAFieldA”:“aValue”}

  • 通过对象映射器运行它:

    readValue(jsonString,TypeC.class)


  • 如何通过循环JsonNode来提取每个字段的完整json字符串?这是最理想的方法吗?

    创建一个Java类,比如JsonNode,并用已知的数据类型及其getter和setter方法定义所有给定的属性。然后用@JsonProperty属性注释每个属性

    复杂对象可以定义为嵌套类,并在JsonNode类中定义为特定嵌套类的属性对象。反过来,对象类可以具有带有@JsonProperty注释的属性

    像这样的-

    class JsonNode {
            @JsonProperty("fieldA")
            private String fieldA;
    
            @JsonProperty("fieldB")
            private int fieldB;
    
            @JsonProperty("fieldC")
            private TypeA fieldC;
    
            @JsonProperty("fieldD")
            private TypeB fieldB;
    
            getters and setters...
    
            class TypeA {
                @JsonProperty("innerFieldA")
                private String innerFieldA;
    
                getters, setters
            }
    
            class TypeA {
                @JsonProperty("innerFieldB")
                private String innerFieldB;
    
                getters, setters
            }
    
    }
    

    您可以这样做:

    ObjectMapper mapper = new ObjectMapper();
    JsonNode actualObj = mapper.readTree(json);
    
    JsonNode fieldA = actualObj.get("fieldA");
    String fieldAObj = fieldA.asText();
    
    JsonNode fieldB = actualObj.get("fieldB");
    Integer fieldBObj = fieldB.asInt();
    
    JsonNode fieldC = actualObj.get("fieldC");
    //if you really want json string of fieldC just use fieldC.toString()
    TypeA fieldCObj = mapper.treeToValue(fieldC, TypeA.class);
    
    JsonNode fieldD = actualObj.get("fieldD");
    TypeB fieldDObj = mapper.treeToValue(fieldD, TypeB.class);
    
    这是100%通用版本:

    JsonNode actualObj = mapper.readTree(json);
    Iterator<Map.Entry<String, JsonNode>> values = actualObj.fields();
    
    Object field;
    while (values.hasNext()){
        Map.Entry<String, JsonNode> entry = values.next();
        String key = entry.getKey();
        JsonNode value = entry.getValue();
    
        if(value.canConvertToInt()){
            // Integer
            field = value.asInt();
        }else if(value.isTextual()){
            // String
            field = value.asText();
        }else{
            try {
                field  = mapper.treeToValue(value, TypeA.class);
            }catch (Exception e){
                field  = mapper.treeToValue(value, TypeB.class);
            }
        }
        System.out.println(key + " => "+ field);
    }
    
    测试代码:

    public class Main {
    private static ObjectMapper mapper = new ObjectMapper();
    private static final String json = "{\n" +
            "    \"fieldA\": \"aStringValue\",\n" +
            "    \"fieldB\": 10,\n" +
            "    \"fieldC\": {\n" +
            "        \"TypeAFieldA\": \"aValue\"\n" +
            "    },\n" +
            "    \"fieldD\": {\n" +
            "        \"TypeBFieldA\": \"aValue\",\n" +
            "        \"TypeBFieldB\": {\n" +
            "            \"TypeCFieldA\": \"aValue\",\n" +
            "            \"TypeCFieldB\": \"bValue\"\n" +
            "        }\n" +
            "    }\n" +
            "}";
    
    public static void main(String[] args) throws IOException, JSONException {
        Data data =  mapper.readValue( json, Data.class );
        String json =  mapper.writeValueAsString(data);
        System.out.println(json);
    }
    
    public static  class TypeA {
        @JsonProperty("TypeAFieldA")
        private String TypeAFieldA;
    
        @Override
        public String toString() {
            return "TypeA{" +
                    "TypeAFieldA='" + TypeAFieldA + '\'' +
                    '}';
        }
    }
    
    public static  class TypeB {
        @JsonProperty("TypeBFieldA")
        private String TypeBFieldA;
    
        @JsonProperty("TypeBFieldB")
        private TypeC TypeBFieldB;
    
        @Override
        public String toString() {
            return "TypeB{" +
                    "TypeBFieldA='" + TypeBFieldA + '\'' +
                    ", TypeBFieldB=" + TypeBFieldB +
                    '}';
        }
    }
    
    public static  class TypeC {
        @JsonProperty("TypeCFieldA")
        private String TypeCFieldA;
    
        @JsonProperty("TypeCFieldB")
        private String TypeCFieldB;
    
        @Override
        public String toString() {
            return "TypeC{" +
                    "TypeCFieldA='" + TypeCFieldA + '\'' +
                    ", TypeCFieldB='" + TypeCFieldB + '\'' +
                    '}';
        }
    }
    }
    
    fieldA aStringValue
    fieldB 10
    fieldC TypeA{TypeAFieldA='aValue'}
    fieldD TypeB{TypeBFieldA='aValue', TypeBFieldB=TypeC{TypeCFieldA='aValue', TypeCFieldB='bValue'}}
    
    结果:

    public class Main {
    private static ObjectMapper mapper = new ObjectMapper();
    private static final String json = "{\n" +
            "    \"fieldA\": \"aStringValue\",\n" +
            "    \"fieldB\": 10,\n" +
            "    \"fieldC\": {\n" +
            "        \"TypeAFieldA\": \"aValue\"\n" +
            "    },\n" +
            "    \"fieldD\": {\n" +
            "        \"TypeBFieldA\": \"aValue\",\n" +
            "        \"TypeBFieldB\": {\n" +
            "            \"TypeCFieldA\": \"aValue\",\n" +
            "            \"TypeCFieldB\": \"bValue\"\n" +
            "        }\n" +
            "    }\n" +
            "}";
    
    public static void main(String[] args) throws IOException, JSONException {
        Data data =  mapper.readValue( json, Data.class );
        String json =  mapper.writeValueAsString(data);
        System.out.println(json);
    }
    
    public static  class TypeA {
        @JsonProperty("TypeAFieldA")
        private String TypeAFieldA;
    
        @Override
        public String toString() {
            return "TypeA{" +
                    "TypeAFieldA='" + TypeAFieldA + '\'' +
                    '}';
        }
    }
    
    public static  class TypeB {
        @JsonProperty("TypeBFieldA")
        private String TypeBFieldA;
    
        @JsonProperty("TypeBFieldB")
        private TypeC TypeBFieldB;
    
        @Override
        public String toString() {
            return "TypeB{" +
                    "TypeBFieldA='" + TypeBFieldA + '\'' +
                    ", TypeBFieldB=" + TypeBFieldB +
                    '}';
        }
    }
    
    public static  class TypeC {
        @JsonProperty("TypeCFieldA")
        private String TypeCFieldA;
    
        @JsonProperty("TypeCFieldB")
        private String TypeCFieldB;
    
        @Override
        public String toString() {
            return "TypeC{" +
                    "TypeCFieldA='" + TypeCFieldA + '\'' +
                    ", TypeCFieldB='" + TypeCFieldB + '\'' +
                    '}';
        }
    }
    }
    
    fieldA aStringValue
    fieldB 10
    fieldC TypeA{TypeAFieldA='aValue'}
    fieldD TypeB{TypeBFieldA='aValue', TypeBFieldB=TypeC{TypeCFieldA='aValue', TypeCFieldB='bValue'}}
    

    受这里发布的解决方案的启发,我能够针对这个问题提出自己的实现

    我编写了一个函数,其中包含一个JsonNode和一个java.lang.reflect.Type参数。该函数将检查节点中我将在应用程序中使用的每个基本数据类型和非基本数据类型,并将其反序列化为适当的类型

    /**
         * This function takes in a JSON node, a type info and converts the JSON into 
         * the given type.
         * @param node - node to deserialize
         * @param typeInfo - data type to deserialize into
         * @throws JsonMappingException
         * @throws JsonParseException
         * @throws IOException
         */
        private void deserializeNode ( JsonNode node, Type typeInfo ) throws JsonMappingException, JsonParseException, IOException {
    
            Object deserializedValue = null;
    
            if ( node.isDouble()   ) {
                deserializedValue = node.asDouble();
    
            } else if ( node.isInt() ) {
                deserializedValue = node.asInt();
    
            } else if ( node.isLong() ) {
                deserializedValue = node.asLong();
    
            } else if ( node.isBoolean() ) {
                deserializedValue = node.asBoolean();
    
            } else if ( node.isArray() ) {
                //Json array is translated into a Java List. If this is a known type, it will translate
                //into a List<Type> instance.
                CollectionType collectionType = this.getActualTypeOfCollection( typeInfo );
                deserializedValue = mapper.readValue( node.toString(),  collectionType );
    
            } else if ( node.isObject() ) {
                JavaType objectType = mapper.getTypeFactory().constructType( typeInfo );
                deserializedValue = mapper.readValue( node.toString(), objectType );
    
            } else if ( node.isTextual() ) {
                deserializedValue = node.asText();
    
            } 
    
            this.deserializedValues.add( deserializedValue );
    
        }
    
    
        /**
         * This function returns the actual collection type of a generic parameter.
         * I.e. It returns the proper Collection data complete with the generic type so
         * that Jackson could determine the proper type to deserialize the field into.
         * @param genericParameterType - java parameter type
         * @return Jackson collection type
         */
        private CollectionType getActualTypeOfCollection ( Type genericParameterType ) {
    
            CollectionType collectionType = null;
    
            if(genericParameterType instanceof ParameterizedType){
    
                ParameterizedType aType = (ParameterizedType) genericParameterType;
                Type[] parameterArgTypes = aType.getActualTypeArguments();
                for ( Type parameterArgType : parameterArgTypes ) {
                    collectionType = mapper.getTypeFactory().constructCollectionType(List.class, (Class<?>) parameterArgType ) ;
                    break;
                }
            }
    
            return collectionType;      
        }
    
    /**
    *此函数接收JSON节点、类型信息并将JSON转换为
    *给定的类型。
    *@param node-要反序列化的节点
    *@param typeInfo-要反序列化为的数据类型
    *@throws-JsonMappingException
    *@抛出JsonParseException
    *@抛出异常
    */
    私有void反序列化节点(JsonNode节点,类型typeInfo)抛出JsonMappingException、JsonParseException、IOException{
    对象反序列化的值=null;
    if(node.isDouble()){
    反序列化的值=node.asDouble();
    }else if(node.isInt()){
    反序列化的值=node.asInt();
    }else if(node.isLong()){
    反序列化的值=node.asLong();
    }else if(node.isBoolean()){
    反序列化的值=node.asBoolean();
    }else if(node.isArray()){
    //Json数组被转换为Java列表。如果这是一个已知类型,它将被转换
    //进入一个列表实例。
    CollectionType CollectionType=this.getActualTypeOfCollection(typeInfo);
    deserializedValue=mapper.readValue(node.toString(),collectionType);
    }else if(node.isObject()){
    JavaType objectType=mapper.getTypeFactory().constructType(typeInfo);
    deserializedValue=mapper.readValue(node.toString(),objectType);
    }else if(node.isTextual()){
    deserializedValue=node.asText();
    } 
    this.deserializedValues.add(deserializedValue);
    }
    /**
    *此函数返回泛型参数的实际集合类型。
    *也就是说,它返回正确的收集数据,并带有泛型类型so
    *Jackson可以确定反序列化字段的正确类型。
    *@param genericParameterType-java参数类型
    *@return-Jackson集合类型
    */
    私有集合类型getActualTypeOfCollection(类型genericParameterType){
    CollectionType CollectionType=null;
    if(genericParameterType instanceof ParameteredType){
    ParameteredType AtType=(ParameteredType)genericParameterType;
    Type[]参数argtypes=aType.getActualTypeArguments();
    对于(类型parameterArgType:parameterArgTypes){
    collectionType=mapper.getTypeFactory().ConstructionCollectionType(List.class,(class)parameterArgType);
    打破
    }
    }
    返回集合类型;
    }
    

    欢迎对这种方法的优缺点发表评论。

    不,它们可以是任何名称。我希望解决方案对字段名称和值都是通用的。您好,感谢您的asnwer。我已经解决了类注释部分。我已经更新了原始问题,以澄清我的要求,因为它之前不明确。您可以传递JsonNode.class作为mapper.read value的参数。该方法将返回一个JsonNode对象。每个属性值都可以通过其getter方法获得。实际上,我的要求是从JsonNode中提取各个字段。例如,要将“fieldC”及其值反序列化为一个TypeC类,我需要完整的Json字符串:“fieldC”:{“TypeAFieldA”“:“aValue”}”,从原始JsonNode.提取,然后通过对象映射器创建类型为c:mapper.readValue(jsonString,TypeC.class)的对象;