GSON:如何从Json获得不区分大小写的元素?
当JSON
对象包含传递给方法的jsonKey
时,下面显示的代码很有效。 我想知道…如果有一种方法可以将值赋给一个不区分大小写的键表示?
例:
public String getOutputEventDescription(JsonElement outputEvent) throws ParserException { return retrieveString(outputEvent, 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(); }
不幸的是,在当前的实现中似乎没有办法做到这一点。 如果你看一下Gson源代码,更具体地说是JsonObject实现,你会发现底层数据结构是一个链接的哈希映射。 get调用只是调用map上的get,而get又使用哈希码和equals方法来查找你要查找的对象。
唯一的方法是为您的密钥强制执行一些命名约定。 最简单的方法是将所有键强制为小写。 如果你需要混合大小写键,那么你将遇到更多困难,需要编写一个更复杂的算法来转换键而不是简单地调用jsonKey.toLowerCase()。
不幸的是,使用GsonBuilder
注册FieldNamingStrategy
并没有太大的好处,因为它仅在与期望相反的方向上进行转换:从Java字段名称到JSON元素名称。 它不能合理地用于您的目的。
(详细:
转换请求的结果在FieldNamingStrategy.translateName(Field)
结束,其中翻译的名称用于从JsonObject
获取关联的JSON元素LinkedHashMap
具有LinkedHashMap
,称为members
,将JSON元素名称映射到它们的相关值。 已翻译的名称用作members
的get(String)
方法的参数,Gson不提供此最终调用的机制,使其不区分大小写。
members
映射填充了对来自Streams.parseRecursive(JsonReader)
JsonObject.add(String, JsonElement)
调用JsonObject.add(String, JsonElement)
从Streams.parseRecursive(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 { @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 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元素名称为全部大写或全部小写。
我遇到了类似的问题。 我这样做是为了解决这个问题。 (将所有键替换为相应的小写版本,并在匹配类中包含所有小写字段)。 希望这可以帮助。
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();