反序列化 JSON 具有随机名称的数组 - GSON

Deserialize JSON Array with random name - GSON

我正在尝试使用 GSON 反序列化一个复杂的 JSON 结构。 API 提供程序通过在结果中提供一个随机名称的数组来使事情复杂化。

这是 (simplified/generified) JSON:

{
    "field_1": "value",
    "field_2": "value",
    "field_3": {
        "RANDOM_NAME": [
            {
                "array_field_1": "value",
                "array_field_2": "value",
                "array_field_3": "value"
            },
            {
                "array_field_1": "value",
                "array_field_2": "value",
                "array_field_3": "value"
            }
        ]
    },
    "field_4": "value"
}

这是对应的(高度简化的)POJO:

public class responseObject {
    String field_1;
    String field_2;
    Field3 field_3;
    String field_4;

    class Field3{
        ArrayObject[] arrayObjects;
    }
    class ArrayObject{
        String array_field_1;
        String array_field_2;
        String array_field_3;
    }
}

然而,当我 运行 responseObject response = new Gson().fromJson(getJSON(),responseObject.class); 我得到以下调用堆栈:

表明 field_3 没有被正确反序列化并且不包含 ArrayObject.

的数组

this post 中的答案参考了如何将数据转换为地图,但在我的例子中,数组中每个项目的数据结构实际上比这个简化的例子大得多,如果我必须从复杂的嵌套地图列表中手动选择我需要的数据,那么它就违背了使用 GSON 的目的。在随机对象是数组而不是普通 json 对象的情况下,也无法让这些答案起作用。

如何获取 JSON 中随机命名的数组以正确反序列化为变量 responseObject.Field3.arrayObjects

这可以通过编写自定义 TypeAdapter for Field3 which ignores the name of the property and only reads the value. The TypeAdapter has to be created by a TypeAdapterFactory 以允许获取 ArrayObject[]:

的委托适配器来解决
class Field3TypeAdapterFactory implements TypeAdapterFactory {
    public Field3TypeAdapterFactory() {
    }

    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        // Only support Field3 class
        if (type.getRawType() != Field3.class) {
            return null;
        }

        TypeAdapter<ArrayObject[]> fieldValueAdapter = gson.getAdapter(ArrayObject[].class);

        // Cast is safe, check at beginning made sure type is Field3
        @SuppressWarnings("unchecked")
        TypeAdapter<T> adapter = (TypeAdapter<T>) new TypeAdapter<Field3>() {
            @Override
            public void write(JsonWriter out, Field3 value) throws IOException {
                throw new UnsupportedOperationException("Serialization is not supported");
            }

            @Override
            public Field3 read(JsonReader in) throws IOException {
                if (in.peek() == JsonToken.NULL) {
                    in.nextNull();
                    return null;
                }

                in.beginObject();
                // Skip the random property name
                in.skipValue();
                ArrayObject[] fieldValue = fieldValueAdapter.read(in);
                in.endObject();

                Field3 object = new Field3();
                object.arrayObjects = fieldValue;
                return object;
            }
        };
        return adapter;
    }
}

然后您可以 register the factory with a GsonBuilder, or you can annotate your Field3 class with @JsonAdapter。使用 @JsonAdapter 时,工厂 class 应该有一个 no-args 构造函数。

您可以通过将类型设为 field_3 Map<String, List<ArrayObject>>

来避免使用 TypeAdapeter 的复杂性
public class responseObject {
    String field_1;
    String field_2;
    Map<String, List<ArrayObject>> field_3;
    String field_4;

    class ArrayObject{
        String array_field_1;
        String array_field_2;
        String array_field_3;
    }
}

然后在不知道其键值的情况下从地图中取出第一项,您可以使用:

public List<ResponseObject.ArrayObject> getFirstValue(Map<String, List<ResponseObject.ArrayObject>> field_3) {
    return field_3.values().iterator().next();
}