在反序列化json

时间:2017-05-19 07:26:56

标签: java spring-boot gson

我在请求体中收到一个无效的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;
    }
}

我试图不注册自定义适配器,因为项目中有很多模型,每个都需要一个适配器,这将是一项重大任务。

1 个答案:

答案 0 :(得分:2)

我很抱歉,但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行路径格式错误的JSON $ .entities [5]

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

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