GSON: How to get a case insensitive element from Json?

Unfortunately, registering a FieldNamingStrategy with the GsonBuilder wouldn’t do much good, as it translates only in the opposite-than-desired direction: from the Java field name to the JSON element name. It cannot be reasonably used for your purposes.

(In Detail:

The result of the translation request ends at FieldNamingStrategy.translateName(Field), where the translated name is used to get the associated JSON element from a JsonObject, which has a LinkedHashMap<String, JsonElement>, called members, mapping JSON element names to their associated values. The translated name is used as the parameter to the get(String) method of members, and Gson provides no mechanism for this final call to be made case insensitive.

The members map is populated with calls to JsonObject.add(String, JsonElement), made from Streams.parseRecursive(JsonReader), with the JSON element name retrieved from the JsonReader used as the key to ‘members’. (JsonReader uses the characters exactly as they are in the JSON, with the exception where the escape character ‘\’ is found.) Throughout this call stack, Gson provides no mechanism for the keys used to populate members to be altered, e.g., to be made all lower case or all upper case.

A FieldNamingPolicy works in the same way.)

A reasonable solution might be to simply use a custom deserializer, along the following lines.

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

Alternatively, you could first process the raw JSON to alter all of the element names to be the same case, all lower or all upper. Then, pass the altered JSON to Gson for deserialization. This would of course slow down JSON processing.

If you’re able to change Gson code for your project, then probably the part to change for the most efficient result is the call to name = nextString((char) quote); in JsonReader. Since nextString(char) is also used to get the JSON element value, I’d probably just make a copy of it for getting the name, and then make small changes to force the element names to all lower or all upper case. Of course, this approach then locks your project to one release of Gson, else you’d need to repeat this change to upgrade to a newer Gson release.

With Jackson, the situation appears unfortunately similar. Translations with a PropertyNamingStrategy work in unfortunately much the same way: they translate from the Java field name to the JSON element name. None of the available JsonParser.Feature alterations would customize a JsonParser to force JSON element names to all upper or all lower case.

Leave a Comment