【问题标题】:Gson deserialize map of objects to map of stringsGson 将对象映射反序列化为字符串映射
【发布时间】:2016-10-31 18:17:45
【问题描述】:

关于 JSON 反序列化,我有一点问题。背景:响应来自图形数据库(ArangoDB,因此它的 JavaScript 服务器端),我通常会使用 JavaScript 客户端处理它。这非常有效,尤其是在查询图形数据时,对象的 JavaScript 概念已被证明非常有用(对我而言)。 IE。我可以以任何对特定端点有用的方式附加边和节点。

这一切都很好,但我开始对服务器进行一些性能测试,并决定为此使用一个简单的 Java 应用程序。但是由于我并不真正使用强类型,因此我无法使其正常工作。基本上我想要的是模仿 JavaScript 概念并将任何对象反序列化为 Map。然后我将反序列化属性,这些属性再次使用与列表相同的机制和属性作为列表。这不需要在对象结构中体现出来,我会在临时变量中即时执行(客户端性能无关紧要)。

问题是 Gson 不反序列化(我知道,它不应该),f.e.

{"a": {"b":"c"}}

使用

private static final Type TYPE = new TypeToken<Map<String, String>>(){}.getType();

到地图 "a"->"{\"b\":\"c\"}"。

Gson 有什么方法可以做到这一点,还是我必须手动解析字符串添加引号和转义其他引号?

我真的不是 RegEx 的粉丝,因此非常感谢任何帮助。

提前致谢, 乔纳斯

【问题讨论】:

  • 我没有完全理解你的问题,但是你真的需要这么严格的类型标记吗?看起来您可以简单地将有效负载反序列化为常规的 Map&lt;String, Object&gt; 策略,据我所知,这是 GSON 中的默认策略。还是我错过了什么?
  • 非常感谢,看来我错过了明显的

标签: json serialization gson deserialization


【解决方案1】:

我现在似乎更好地理解了您的问题。仍然不确定您的代码的真正目的,所以我想在这里做一些假设,因为 cmets 太短了。

不幸的是,Java 的静态类型特性不允许您像在 JavaScript 中那样编写那么短的代码。但是,您可以遵循惯用的方式,也可以采用某种变通方法。

简单的 Java

您不能使用new TypeToken&lt;Map&lt;String, String&gt;&gt;(){}.getType() 的原因是GSON 希望始终解析一个普通的字符串到字符串的映射(Map&lt;String,String&gt;)。因此{"a": "b"} 很好,但{"a":{"b":"c"}} 不是。在解析这样的 JSON 时,您应该对每个地图值类型做出一些假设/期望。默认情况下,GSON 为 Map.class 目标类型使用 Map&lt;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&lt;String, String&gt;) outerMap.get("a") 才能起作用(由于 Java 中泛型的性质,不管它是用什么参数化的)。

替代方案:包装器

以下方式重新考虑您尝试反序列化的对象模型。然而,这种方式对你来说可能有点过头了,但它使类型分析本身摆脱了手动类型检查。另一个缺点是它需要创建另外两个不同的地图(但至少它会创建一次)。一个优点是您可以以或多或少方便的方式区分“节点”和“值”映射。以下包装器实现丢失了原始的Map&lt;String, ...&gt; 接口,但如果它没问题,你可以让它实现它(它肯定需要在 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


而且,作为结论,静态类型需要更多的工作,并且在某些情况下可能看起来不太方便。

【讨论】:

  • 非常感谢您的回答!像魅力一样工作!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-18
  • 2014-02-08
  • 2018-08-06
相关资源
最近更新 更多