Я хочу предотвратить такую «инъекцию json» при десериализации данных JSON. По умолчанию парсеры json не заботятся о дублированных ключах в объекте и просто перезаписывают их.
Я потратил некоторое время на то, чтобы найти функцию, обращающую это поведение вспять. Я добился успеха с Джексоном, но не с Гсоном. Проблема в том, что большая часть кода в проекте основана на Gson. Так что переключиться на Джексона непросто.
В Джексоне эта функция называется "STRICT_DUPLICATE_DETECTION"
{"x": true, "x": false}
Вроде, как бы, что-то вроде.
По какой-то причине Gson обнаруживает конфликты имен при десериализации экземпляров Map
, но не выполняет такую проверку для пакетов данных или JsonElement
и его потомков (поэтому вы не можете написать десериализатор JSON на основе дерева для их обнаружения).
Данный,
{
"x": true,
"x": false
}
final class Foo {
@SuppressWarnings("UnnecessaryBoxing")
final boolean x = Boolean.valueOf(false);
}
final Foo foo = gson.fromJson(jsonReader, Foo.class);
if ( !foo.x ) {
throw new AssertionError();
}
Это не подходит для стандартного JsonReader
.
Однако вы можете обойти это на самом низком уровне.
Это немного сложно и плохо оптимизировано, но охватывает все возможные десериализаторы (и это, вероятно, объясняет, почему Gson ведет себя по-разному в разных десериализаторах: каждый такой десериализатор должен сам выполнять эту проверку и может просто пропустить задание).
final class StrictNamesJsonReader
extends JsonReader {
private final Stack<Set<String>> nameStack = new Stack<>();
private Set<String> names;
private StrictNamesJsonReader(final Reader reader) {
super(reader);
}
static JsonReader of(final Reader reader) {
return new StrictNamesJsonReader(reader);
}
@Override
public void beginObject()
throws IOException {
super.beginObject();
names = new HashSet<>();
nameStack.add(names);
}
@Override
public String nextName()
throws IOException {
final String name = super.nextName();
if ( !names.add(name) ) {
throw new JsonSyntaxException("Detected duplicate property name " + name + " in " + names + " at " + this);
}
return name;
}
@Override
public void endObject()
throws IOException {
super.endObject();
nameStack.pop();
names = !nameStack.isEmpty() ? nameStack.peek() : null;
}
}
Как видите, эта реализация JsonReader
сама управляет обнаружением дубликатов имен и генерирует что-то вроде следующего исключения
Exception in thread "main" com.google.gson.JsonSyntaxException: Detected duplicate property name x in [x] at StrictNamesJsonReader at line 3 column 5 path $.x
если обнаружены дубликаты.
Однако вы можете попросить пересмотреть свой вопрос на https://github.com/google/gson/pull/649.
Я действительно считаю, что десериализаторы - неправильный способ обнаружения дубликатов, и JsonReader
следует улучшить в отличие от того, что предлагается в этом PR.