想象一下,有一个 JSON 结构,其中包含客户购买的产品列表:
{
"product_key_1": {
"name": "product_name_1",
"price": 10.99
},
"product_key_2": {
"name": "product_name_2",
"price": 24.99
},
"product_key_3": {
"name": "product_name_3",
"price": 7.49
}
}
但是,在这种情况下,客户端只对提取代表产品的密钥感兴趣,而不关心产品名称或价格等具体细节。
使用 Gson 反序列化此 JSON 字符串的推荐方法是什么,主要目标是获取密钥?
目前我正在采用以下方法:
Map<String, Object> products = gson.fromJson(rawJsonString, Object.class);
Set<String> productKeys = products.keySet();
这可行,但我想知道这是否是正确的方法。”
编辑:
似乎另一种方法是将 json 转换为 Map
Map<String, JsonElement> products = gson.fromJson(rawJsonString, new TypeToken<Map<String, JsonElement>>() {}.getType());
如果你只对地图键感兴趣,那么将地图值反序列化为
Object
或JsonElement
是相当浪费的,因为Gson仍然会构造相应的对象,即使你不使用它们。
您可以做的是创建一个空类并将其指定为值类型(假设映射值始终为 JSON 对象类型)。因为 Gson 默认情况下会忽略未知字段,所以它将有效地跳过 JSON 对象成员值中的所有字段。
代码可能如下所示:
class IgnoredValue {
}
Map<String, IgnoredValue> map = gson.fromJson(rawJsonString, new TypeToken<Map<String, IgnoredValue>>() {});
System.out.println(map.keySet());
TypeAdapter
,它始终会跳过 JSON 值并返回 null
。如果映射值不是 JSON 对象(例如 JSON 数组、数字或字符串),这可能会更有效,并且也可以工作:
Gson gson = new GsonBuilder()
.registerTypeAdapter(IgnoredValue.class, new TypeAdapter<IgnoredValue>() {
@Override
public IgnoredValue read(JsonReader in) throws IOException {
in.skipValue();
return null;
}
@Override
public void write(JsonWriter out, IgnoredValue value) throws IOException {
throw new UnsupportedOperationException();
}
})
.create();
Map<String, IgnoredValue> map = gson.fromJson(rawJsonString, new TypeToken<Map<String, IgnoredValue>>() {});
或者,如果此 JSON 数据位于顶层(并且没有深深嵌套在较大的 JSON 文档中),那么如果您愿意,您也可以直接使用
JsonReader
:
List<String> keys = new ArrayList<>();
try (JsonReader jsonReader = new JsonReader(new StringReader(rawJsonString))) {
jsonReader.beginObject();
// Handle all JSON object members
while (jsonReader.hasNext()) {
keys.add(jsonReader.nextName());
jsonReader.skipValue();
}
jsonReader.endObject();
}
如果 JSON 数据嵌套得很深,并且您有一个封闭的反序列化类,那么您可以添加一个
List<String> products
字段,创建一个 TypeAdapter
,其中在其 JsonReader
方法中包含上述 read
代码,然后添加一个 @JsonAdapter
注释引用该字段上的类型适配器。