【问题标题】:GSON: How to get a case insensitive element from Json?GSON:如何从 Json 中获取不区分大小写的元素?
【发布时间】:2011-09-14 00:42:08
【问题描述】:

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

【问题讨论】:

    标签: java json gson case-insensitive


    【解决方案1】:

    不幸的是,将FieldNamingStrategy 注册到GsonBuilder 并没有多大好处,因为它只会在与预期相反的方向上进行转换:从Java 字段名称到JSON 元素名称。它不能被合理地用于您的目的。

    (详细:

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

    members 映射填充了对JsonObject.add(String, JsonElement) 的调用,由Streams.parseRecursive(JsonReader) 生成,从JsonReader 检索到的 JSON 元素名称用作“成员”的键。 (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 代码,那么为了最有效的结果而更改的部分可能是在 JsonReader 中调用 name = nextString((char) quote);。由于nextString(char) 也用于获取 JSON 元素值,我可能只是复制它以获取名称,然后进行一些小的更改以强制元素名称全部小写或全部大写。当然,这种方法会将您的项目锁定到一个 Gson 版本,否则您需要重复此更改才能升级到更新的 Gson 版本。

    对于Jackson,不幸的是,情况似乎相似。不幸的是,带有PropertyNamingStrategy 的翻译工作方式大致相同:它们从 Java 字段名称转换为 JSON 元素名称。可用的 JsonParser.Feature 更改都不会自定义 JsonParser 以强制 JSON 元素名称全部为大写或全部小写。

    【讨论】:

      【解决方案2】:

      我遇到了类似的问题。我这样做是为了解决这个问题。 (将所有键替换为相应的小写版本,并在匹配类中包含所有小写字段)。希望这会有所帮助。

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

      【讨论】:

      • 这很好用,只是我不确定你为什么要删除所有空格。
      【解决方案3】:

      不幸的是,目前的实现似乎没有办法做到这一点。如果您查看 Gson 源代码,更具体地查看 JsonObject 实现,您会看到底层数据结构是一个链接的哈希映射。 get 调用只是调用 map 上的 get,然后使用 key 的哈希码和 equals 方法来查找您要查找的对象。

      唯一的解决方法是为您的键强制执行一些命名约定。最简单的方法是强制所有键为小写。如果您需要混合大小写的键,那么您将遇到更多困难,并且需要编写更复杂的算法来转换键,而不是简单地调用 jsonKey.toLowerCase()。

      【讨论】:

        【解决方案4】:

        当我遇到两个端点使用不同的命名约定并随后发现侵入性较小的解决方案时,我偶然发现了这个问题。

        Gson 确实支持设置从 Java 模型名称映射到 JSON 名称时使用的命名约定,无论是在序列化和反序列化时。使用构建器的 setFieldNamingPolicy 方法来改变这种行为。

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

        请参阅here 了解有关该主题的精彩文章,其中包括对不同政策的概述。

        这并不是真正区分大小写的解决方案,但它确实提供了一种解决许多大小写不匹配的情况的方法。

        【讨论】:

          猜你喜欢
          • 2010-10-26
          • 1970-01-01
          • 2021-11-01
          • 1970-01-01
          • 2012-02-03
          • 1970-01-01
          • 1970-01-01
          • 2015-04-08
          相关资源
          最近更新 更多