当前位置: 首页 > 知识库问答 >
问题:

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

荆乐
2023-03-14

我有一个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等创建对象,并用相关的JsonPropery注释对它们进行注释。我不清楚的是,如何单独反序列化每个字段?为此,我需要从JsonNode中逐个提取json字符串,并运行具有相关类类型的对象映射器?

示例:要将“fieldc”及其值反序列化为一个TypeC类,难道我不需要做如下操作吗:

>

  • 提取完整的Json字符串:

    字符串jsonString=“fieldc”:{“typeaFielda”:“avalue”}“;

    通过对象映射器运行它:

    Mapper.ReadValue(jsonString,TypeC.Class);

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

  • 共有2个答案

    申屠飞
    2023-03-14

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

    我编写了一个函数,它接受一个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;      
        }
    

    欢迎就这种方法的利弊提出意见。

    查飞星
    2023-03-14

    您可以这样做:

    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);
    }
    

    或者,您可以将父对象与@JSONanySetter一起使用,并将确定对象类型和创建对象实例的所有逻辑放在这个setter中。这是演示

    public static class Data{
        private  HashMap<String,Object> data = new HashMap<String, Object>();
    
        @JsonAnyGetter
        public HashMap<String, Object> getValues(){
            return data;
        }
    
        @JsonAnySetter
        public void setValue(String key, JsonNode value) {
            // value.toString() is json string of each field
            Object resultObj = "";
    
            if (value.canConvertToInt()) {
                resultObj = String.valueOf(value);
            } else if (value.isTextual()) {
                resultObj = String.valueOf(value);
            } else if (value.has("TypeAFieldA")) {
                try {
                    resultObj = mapper.treeToValue(value, TypeA.class);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else if (value.has("TypeBFieldB")) {
                try {
                    resultObj = mapper.treeToValue(value, TypeB.class);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            System.out.println(key + " " + resultObj);
            // can use key - resultObj pair any way you want
            //for example add it to hashmap or multiple hashmaps for each class type
            data.put(key, resultObj);
        }
    }
    

    测试代码:

    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'}}
    
     类似资料:
    • 我之前的问题没有得到任何结果,可能是我问得不对,或者太详细了。 现在我的基本问题是,如何将一个具有一个类名的JSON对象反序列化为一个具有不同类名的Java对象? 例如,服务器发送/期望的JSON类是“18”(我对此控制为零)。我的Java班不可能是“18”,所以是“_18”。 简单地执行new Gson().fromjson()没有任何帮助,即使使用了不同的命名策略;_18对象始终为NULL。从

    • 问题内容: 如何将上述字符串反序列化为java对象。 我正在使用的类是 问题答案: @基达 我假设您可以控制JSON输入字符串的创建方式。我认为JSON字符串格式不正确,无法对地图类型进行默认的GSON反序列化。 我已经修改了输入字符串供您考虑,这将导致非null的LocalLocationId 如果我对输入字符串的假设不正确,请发表评论。 编辑1:由于无法修改输入,请考虑编写自定义解串器。以下是

    • 我的JSON如下所示: 我的课程如下所示: 如何反序列化到映射如和到

    • 问题内容: 我有这个字符串: 我正在反序列化对象: 该对象看起来像: 并尝试创建字典: 但得到。 可能是什么问题? 问题答案: 请参阅mridula的答案,了解为什么您会得到null。但是,如果您想直接将json字符串转换为字典,则可以尝试以下代码段。

    • 问题内容: 有人知道如何基于NSObject类序列化嵌套JSON吗?有序列化JSON简单的讨论在这里,但它不是一般的足够,以满足复杂的嵌套JSON。 想象这是JSON的结果: 从这个班级: 如何基于类对它们进行序列化/反序列化? 编辑 目前,我可以基于任何类生成这样的有效负载: 但是它不能满足嵌套的复杂JSON的要求。有人对此有更好的解决方案吗?该 C#库可根据对象类进行序列化/反序列化。我想基于

    • 我试图反序列化2种不同类型的列表,包括它们的派生类 为了更好地解释它,我做了下面的例子 我有2个系统: API系统 与下列实体合作: 应用系统 使用以下DTO 使用此示例,API系统返回一个