GSON: Как получить нечувствительный к регистру элемент от Json? - PullRequest
12 голосов
/ 13 июня 2011

Код, показанный ниже, хорошо работает, когда объект JSON содержит jsonKey, как он был передан методу. Интересно ... есть ли способ получить значение, назначенное нечувствительному к регистру представлению ключа?

Пример:

public String getOutputEventDescription(JsonElement outputEvent) throws ParserException {
    return retrieveString(outputEvent, DESCRIPTION);
}

Должно работать независимо от того, определено ли DESCRIPTION как "Description", "description" или "DeScRipTIOn"

protected String retrieveString(JsonElement e, String jsonKey) throws ParserException {

JsonElement value = e.getAsJsonObject().get(jsonKey);

if (value == null) {
    throw new ParserException("Key not found: " + jsonKey);
}

if (value.getAsString().trim().isEmpty()) {
    throw new ParserException("Key is empty: " + jsonKey);
}

return e.getAsJsonObject().get(jsonKey).getAsString();
}

Ответы [ 3 ]

8 голосов
/ 14 июня 2011

К сожалению, регистрация FieldNamingStrategy с помощью GsonBuilder не принесет большой пользы, поскольку она переводит только в противоположном направлении: от имени поля Java к элементу JSON название. Он не может быть разумно использован для ваших целей.

(Подробно:

Результат запроса на перевод заканчивается на FieldNamingStrategy.translateName(Field), где переведенное имя используется для получения связанного элемента JSON из JsonObject, который имеет LinkedHashMap<String, JsonElement>, называемый members, сопоставляя имена элементов JSON с их связанные ценности. Переведенное имя используется в качестве параметра для get(String) метода members, и Gson не предоставляет никакого механизма для того, чтобы этот последний вызов не учитывал регистр.

Карта members заполняется вызовами JsonObject.add(String, JsonElement), сделанными из Streams.parseRecursive(JsonReader), с именем элемента JSON, полученным из JsonReader, используемого в качестве ключа для «members». (JsonReader использует символы в точности так, как они есть в JSON, за исключением того, что находится escape-символ '\'.) В этом стеке вызовов Gson не предоставляет механизма для изменения ключей, используемых для заполнения members. Например, должен быть весь нижний регистр или весь верхний регистр.

A FieldNamingPolicy работает таким же образом.)

Разумное решение может состоять в том, чтобы просто использовать пользовательский десериализатор, как показано ниже.

input.json:

[
 {"field":"one"},
 {"Field":"two"},
 {"FIELD":"three"},
 {"fIElD":"four"}
]

Foo.java:

import java.io.FileReader;
import java.lang.reflect.Type;
import java.util.Map.Entry;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;

public class Foo
{
  public static void main(String[] args) throws Exception
  {
    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(MyClass.class, new MyTypeAdapter());
    Gson gson = gsonBuilder.create();
    MyClass[] myObjects = gson.fromJson(new FileReader("input.json"), MyClass[].class);
    System.out.println(gson.toJson(myObjects));
  }
}

class MyClass
{
  String field;
}

class MyTypeAdapter implements JsonDeserializer<MyClass>
{
  @Override
  public MyClass deserialize(JsonElement json, Type myClassType, JsonDeserializationContext context)
      throws JsonParseException
  {
    // json = {"field":"one"}
    JsonObject originalJsonObject = json.getAsJsonObject();
    JsonObject replacementJsonObject = new JsonObject();
    for (Entry<String, JsonElement> elementEntry : originalJsonObject.entrySet())
    {
      String key = elementEntry.getKey();
      JsonElement value = originalJsonObject.get(key);
      key = key.toLowerCase();
      replacementJsonObject.add(key, value);
    }
    return new Gson().fromJson(replacementJsonObject, MyClass.class);
  }
}

В качестве альтернативы, вы могли бы сначала обработать необработанный JSON, чтобы изменить все имена элементов на один и тот же регистр, все нижнее или все верхнее. Затем передайте измененный JSON в Gson для десериализации. Это, конечно, замедлит обработку JSON.

Если вы можете изменить код Gson для своего проекта, то, вероятно, часть, которую нужно изменить для получения наиболее эффективного результата, - это вызов name = nextString((char) quote); в JsonReader. Поскольку nextString(char) также используется для получения значения элемента JSON, я, вероятно, просто сделаю его копию для получения имени, а затем внесу небольшие изменения, чтобы заставить имена элементов указывать все в нижнем или верхнем регистре. Конечно, этот подход затем привязывает ваш проект к одному выпуску Gson, иначе вам придется повторить это изменение, чтобы перейти на более новый выпуск Gson.

С Джексоном ситуация выглядит, к сожалению, похожей. Переводы с PropertyNamingStrategy работают, к сожалению, почти одинаково: они переводятся из имени поля Java в имя элемента JSON. Ни одно из доступных изменений JsonParser.Feature не настроит JsonParser для принудительной установки имен элементов JSON в верхний или нижний регистр.

7 голосов
/ 30 июля 2013

Я столкнулся с подобной проблемой.Я сделал это, чтобы обойти проблему.(Заменены все ключи на соответствующие им строчные буквы и все строчные поля в соответствующем классе).Надеюсь, это поможет.

 input = input.replaceAll("\\s","");
        Matcher m = Pattern.compile("\"\\b\\w{1,}\\b\"\\s*:").matcher(input);
        StringBuilder sanitizedJSON = new StringBuilder();
        int last = 0;
        while (m.find()) {
            sanitizedJSON.append(input.substring(last, m.start()));
            sanitizedJSON.append(m.group(0).toLowerCase());
            last = m.end();
        }
        sanitizedJSON.append(input.substring(last));

        input = sanitizedJSON.toString();
5 голосов
/ 13 июня 2011

К сожалению, в текущей реализации, похоже, нет способа сделать это.Если вы посмотрите на источник Gson, а точнее на реализацию JsonObject, то увидите, что основная структура данных представляет собой связанную хэш-карту.Вызов get просто вызывает метод get on the map, который, в свою очередь, использует хеш-код и метод equals вашего ключа для поиска искомого объекта.

Единственный выход состоит в том, чтобы применить некоторые соглашения об именахты ключи.Самый простой способ состоит в том, чтобы заставить все ключи в нижнем регистре.Если вам нужны ключи со смешанным регистром, у вас будет больше трудностей, и вам нужно будет написать более сложный алгоритм преобразования ключей вместо простого вызова jsonKey.toLowerCase ().

...