GSON:如何从Json中获取不区分大小写的元素?

16

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();
}
4个回答

8

很遗憾,使用 GsonBuilder 注册 FieldNamingStrategy 并没有什么用处,因为它只能将 Java 字段名翻译成 JSON 元素名,无法满足您的需求。

具体来说:

翻译请求的结果在 FieldNamingStrategy.translateName(Field) 结束,翻译后的名称用于从 JsonObject 中获取关联的 JSON 元素。该对象有一个 LinkedHashMap<String, JsonElement>,称为 members,将 JSON 元素名称映射到它们的关联值。翻译后的名称作为 members 的参数传递给 get(String) 方法,并且 Gson 不提供使此最终调用不区分大小写的机制。

members 映射通过对 JsonObject.add(String, JsonElement) 的调用进行填充,该方法从 Streams.parseRecursive(JsonReader) 调用中获得 JSON 元素名称并将其用作“members”的键。(JsonReader 使用与 JSON 中完全相同的字符,除非发现转义字符“\”)。在整个调用堆栈中,Gson 不提供修改用于填充 members 的键的机制,例如将其全部转换为小写或大写。

类似地,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版本。

使用Jackson,情况似乎非常相似。使用PropertyNamingStrategy进行翻译的方式不幸地相同:它们从Java字段名称转换为JSON元素名称。没有可用的JsonParser.Feature更改会自定义JsonParser以将JSON元素名称强制为全部大写或全部小写。


8

我遇到了类似的问题。为了解决这个问题,我做了以下操作:将所有的键替换为它们对应的小写版本,并在匹配类中使用小写字段。希望这可以帮助你。

 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();

1
这个很好运行,只是我不确定你为什么要移除所有的空格。 - Rooster242

5
很遗憾,当前实现似乎没有办法做到这一点。如果您查看Gson源代码,特别是JsonObject实现,您将看到底层数据结构是一个链接哈希映射。get调用只是在映射上调用get,后者又使用键的哈希码和equals方法来查找您要查找的对象。
唯一的解决办法是为您的键强制执行一些命名约定。最简单的方式是将所有键强制转换为小写。如果您需要大小写混合的键,则会更加困难,并且需要编写更复杂的算法来转换键,而不仅仅是调用jsonKey.toLowerCase()。

3

当我遇到两个端点使用了不同的命名约定时,我偶然发现了一种较少侵入性的解决方案。我在这个问题上跌跌撞撞地前进。

Gson支持设置一个命名约定,用于从Java模型名称映射到JSON名称,无论是序列化还是反序列化。使用建造者(setFieldNamingPolicy方法)来更改这种行为。

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE);
Gson gson = gsonBuilder.create();

请查看这里的一篇很好的文章,介绍了不同策略的概述。

虽然这并不是一个不区分大小写的解决方案,但它提供了一种解决许多情况下大小写不匹配的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接