【问题标题】:exclude NULL from JsonArray while de-serializing json在反序列化 json 时从 JsonArray 中排除 NULL
【发布时间】:2017-10-19 05:23:49
【问题描述】:

我在请求正文中收到一个无效的 json(列表末尾的额外逗号),它正在被 GSON 库成功反序列化。经过检查,我看到 GSON 最后插入了一个 NULL 对象。

{
    "content": "Test 2",
    "timestamp": 1494311947530,
    "entities": [
        {"name": "entity1"},
        {"name": "entity2"},
        {"name": "entity3"},
        {"name": "entity4"},
        {"name": "entity5"},
    ]
}

有没有一种方法可以指示 GSON 不接受无效的 json 或从 JsonArray 中删除 NULL 对象。

我已尝试为 Set.class 注册类型适配器,但我无法继续使用此解决方案,因为无法获取参数化对象的 Type

public class RemoveNullCollectionSerializer<T> implements JsonDeserializer<Set<T>> {
    @Override
    public Set<T> deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) throws JsonParseException {
        JsonArray elements = jsonElement.getAsJsonArray();

        Set<T> result = new HashSet();
        for (JsonElement element : elements) {
            if (element.isJsonNull()) continue;

            T value = (T) context.deserialize(element, Object.class);
            result.add(value);
        }
        return result;
    }
}

我正在尝试不注册自定义适配器,因为项目中有很多模型,每个模型都需要一个适配器,这将是一项艰巨的任务。

【问题讨论】:

  • 可以使用正则表达式从序列化字符串中删除无效逗号。恐怕我不知道确切的语法。
  • json 是来自 API 还是什么?只是想弄清楚你是如何收到无效的 json 的。我不知道 Gson 中有任何内置函数允许您跳过空值(自定义反序列化程序除外),但是,Genson (owlike.github.io/genson) 的工作方式非常相似,但实际上 确实如此 允许您跳过空值。
  • 这是向用户公开的公共 api 的一部分。由于应用程序中的错误导致 API 端点抛出 NPE,因此从用户应用程序接收到 json。服务高度依赖GSON,用Genson替代有点困难。

标签: java spring-boot gson


【解决方案1】:

很抱歉,Gson 似乎无法做到这一点。 Gson 中有一个特殊模式,指示它在“宽松”模式下工作——这就是为什么你会在结果集合中得到一个 null 元素。宽松模式告诉 Gson 忽略一些软无效 JSON 问题,其中之一是读取尾随数组元素和对象属性。如果您查看实际上负责集合readCollectionTypeAdapterFactory,您会看到:

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

  Collection<E> collection = constructor.construct();
  in.beginArray();
  while (in.hasNext()) {
    E instance = elementTypeAdapter.read(in);
    collection.add(instance);
  }
  in.endArray();
  return collection;
}

如您所见,元素 instance 被读取并总是添加到结果集合中。 elementTypeAdapter.read 在非宽松模式下可能会失败,但这意味着不会完全构造对象。你可以这样检查:

private static final Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(new TypeAdapterFactory() {
            @Override
            public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
                if ( !Collection.class.isAssignableFrom(typeToken.getRawType()) ) {
                    return null;
                }
                final TypeAdapter<T> delegateAdapter = gson.getDelegateAdapter(this, typeToken);
                return new TypeAdapter<T>() {
                    @Override
                    public void write(final JsonWriter out, final T value)
                            throws IOException {
                        delegateAdapter.write(out, value);
                    }

                    @Override
                    public T read(final JsonReader in)
                            throws IOException {
                        final boolean wasLenient = in.isLenient();
                        try {
                            in.setLenient(false);
                            return delegateAdapter.read(in);
                        } finally {
                            in.setLenient(wasLenient);
                        }
                    }
                };
            }
        })
        .create();

请注意,read 方法会暂时禁用宽松模式,然后再将其恢复。上面的代码会导致

使用 JsonReader.setLenient(true) 在第 20 行第 3 列路径 $.entities[5] 处接受格式错误的 JSON

为您提供尾随“空虚”的 JSON 文档。根据 Gson 中的宽松模式,无法检查下一个 JSON 令牌是否会失败。本身并不是最好的(但可能是唯一的方法)是使用反射来获得JsonReader 支持阅读器(实际上是Reader)并用新的JsonReader 覆盖hasNext() 方法或类似的方法来装饰它。如果JsonReader 支持一种方法来检查它的最后一个值是否由于宽松模式设置为真而生成,那就太好了。但即使它支持类似的东西,也不能保证会删除最后一个元素,因为特定类型的适配器可能会返回一个不可修改的集合。

顺便说一句,我认为这个问题应该发布到https://github.com/google/gson/issues/ -- 我很想得到 Gson 团队的反馈。

【讨论】:

  • 感谢您如此详细的解释。我将尝试上述解决方案,同时我在他们的 github 页面上创建了提出的关注 Github
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-12
  • 1970-01-01
  • 1970-01-01
  • 2011-05-14
  • 2017-12-14
  • 1970-01-01
相关资源
最近更新 更多