我现在似乎更好地理解了您的问题。仍然不确定您的代码的真正目的,所以我想在这里做一些假设,因为 cmets 太短了。
不幸的是,Java 的静态类型特性不允许您像在 JavaScript 中那样编写那么短的代码。但是,您可以遵循惯用的方式,也可以采用某种变通方法。
简单的 Java
您不能使用new TypeToken<Map<String, String>>(){}.getType() 的原因是GSON 希望始终解析一个普通的字符串到字符串的映射(Map<String,String>)。因此{"a": "b"} 很好,但{"a":{"b":"c"}} 不是。在解析这样的 JSON 时,您应该对每个地图值类型做出一些假设/期望。默认情况下,GSON 为 Map.class 目标类型使用 Map<String, Object 映射,因此以下代码有效,您必须自己检查值类型才能检查是否可以更深入。
final Gson gson = new Gson();
final String json = "{\"a\": {\"b\":\"c\"}}";
@SuppressWarnings("unchecked")
final Map<String, Object> outerMap = gson.fromJson(json, Map.class);
@SuppressWarnings("unchecked")
final Map<String, String> innerMap = (Map<String, String>) outerMap.get("a");
out.println(innerMap.get("b"));
c
请注意上面的类型转换,只有当 a 子确实是 Map 时,(Map<String, String>) outerMap.get("a") 才能起作用(由于 Java 中泛型的性质,不管它是用什么参数化的)。
替代方案:包装器
以下方式重新考虑您尝试反序列化的对象模型。然而,这种方式对你来说可能有点过头了,但它使类型分析本身摆脱了手动类型检查。另一个缺点是它需要创建另外两个不同的地图(但至少它会创建一次)。一个优点是您可以以或多或少方便的方式区分“节点”和“值”映射。以下包装器实现丢失了原始的Map<String, ...> 接口,但如果它没问题,你可以让它实现它(它肯定需要在 API 完整度和性能/内存命中之间找到一个折衷方案)
final class Wrapper {
private final Map<String, String> values;
private final Map<String, Wrapper> wrappers;
private Wrapper(final Map<String, String> values, final Map<String, Wrapper> wrappers) {
this.values = values;
this.wrappers = wrappers;
}
static Wrapper wrap(final Map<String, ?> map) {
final Map<String, String> values = new LinkedHashMap<>();
final Map<String, Wrapper> wrappers = new LinkedHashMap<>();
for ( final Entry<String, ?> e : map.entrySet() ) {
final String key = e.getKey();
final Object value = e.getValue();
if ( value instanceof String ) {
values.put(key, (String) value);
} else if ( value instanceof Map ) {
@SuppressWarnings("unchecked")
final Map<String, ?> m = (Map<String, ?>) value;
wrappers.put(key, wrap(m));
} else {
throw new IllegalArgumentException("The value has inappropriate type: " + value.getClass());
}
}
return new Wrapper(values, wrappers);
}
String valueBy(final String key) {
return values.get(key);
}
Wrapper wrapperBy(final String key) {
return wrappers.get(key);
}
}
然后:
final Gson gson = new Gson();
final String json = "{\"a\": {\"b\":\"c\"}}";
@SuppressWarnings("unchecked")
final Map<String, ?> outerMap = (Map<String, ?>) gson.fromJson(json, Map.class);
final Wrapper outerWrapper = wrap(outerMap);
final Wrapper innerWrapper = outerWrapper.wrapperBy("a");
out.println(innerWrapper.valueBy("b"));
c
包装反序列化器
手动包装很好,但您可以使用包装反序列化器使其更加“Gson-ish”:
final class WrapperDeserializer
implements JsonDeserializer<Wrapper> {
private final Gson backingGson;
private WrapperDeserializer(final Gson backingGson) {
this.backingGson = backingGson;
}
static JsonDeserializer<Wrapper> getWrapperDeserializer(final Gson backingGson) {
return new WrapperDeserializer(backingGson);
}
@Override
public Wrapper deserialize(final JsonElement json, final Type type, final JsonDeserializationContext context) {
@SuppressWarnings("unchecked")
final Map<String, ?> map = backingGson.fromJson(json, Map.class);
return wrap(map);
}
}
请注意,它使用支持 Gson 实例来反序列化 Map 实例(json.getAsJsonObject().entrySet() 将不起作用,因为它将包含 JsonObject 实例作为值,因此使 Wrapper.wrap 方法能够识别 GSON 没有任何真正的原因-- 这就是为什么最好将其转换为“本机”JDK 类的原因)。然后:
final Gson gson = new GsonBuilder()
.registerTypeAdapter(Wrapper.class, getWrapperDeserializer(new Gson()))
.create();
final String json = "{\"a\": {\"b\":\"c\"}}";
final Wrapper outerWrapper = (Wrapper) gson.fromJson(json, Wrapper.class);
final Wrapper innerWrapper = outerWrapper.wrapperBy("a");
out.println(innerWrapper.valueBy("b"));
c
而且,作为结论,静态类型需要更多的工作,并且在某些情况下可能看起来不太方便。