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

时间:2016-04-08 13:25:09

标签: java json jackson

我有一个可以包含任何JSON内容的JSONNode对象。示例:

{
    "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);

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

3 个答案:

答案 0 :(得分:3)

您可以这样做:

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'}}

答案 1 :(得分:2)

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

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

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

答案 2 :(得分:1)

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

}