繁体   English   中英

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

[英]How to deserialize JSON string into different complex class types?

我有一个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类,请不要执行以下操作:

  1. 提取完整的Json字符串:

    String jsonString =“fieldC”:{“TypeAFieldA”:“aValue”}“;

  2. 通过对象映射器运行它:

    mapper.readValue(jsonString,TypeC.class);

如何通过循环遍历JsonNode为每个字段提取完整的json字符串? 这是解决这个问题的最佳方式吗?

你可以这样做:

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一起@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'}}

受到此处发布的解决方案的启发,我能够针对该问题提出自己的实施方案。

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

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

创建一个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
        }

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM