Nelson Tatius
Nelson Tatius

Reputation: 8053

Gson: specify naming policy for class or field

I have the misfortune to parse JSON data where different naming policies coexist.

{
  "camelCaseData" : {
    "someField" : 1
  },
  "snake_case_data" : {
    "some_field" : 2
  }
}

Is it possible to specify naming policy for the whole snakeCaseData? Or another solution that helps to avoid manual annotating of each field by @SerializedName?

Something like the following

class Data {
  CamelCaseData camelCaseData;

  @GsonNamingPolicy(com.google.gson.FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES )
  SnakeCaseData snakeCaseData;
}

Upvotes: 4

Views: 3778

Answers (1)

andersschuller
andersschuller

Reputation: 13907

I can't get to exactly what you were asking for, but I think this might help you anyway. Basically this would require you to annotate any class that contains only snake-case fields, and any snake-case fields in classes that also contain camel-case fields.

First we define an annotation like you suggested:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface GsonNamingPolicy {
    FieldNamingPolicy value();
}

Then we would annotate the classes as I described above:

public static class CamelCaseData {
    int someField;
}

@GsonNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
public static class SnakeCaseData {
    int someField;
}

public static class Data {
    CamelCaseData camelCaseData;

    @GsonNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
    SnakeCaseData snakeCaseData;
}

Finally we define a custom FieldNamingStrategy, which checks the field and its declaring class for our new annotation. If the annotation is present, the policy defined in the annotation is used, otherwise it just uses a default policy.

public static class AnnotationFieldNamingStrategy implements FieldNamingStrategy {
    public String translateName(Field field) {
        Class<?> declaringClass = field.getDeclaringClass();

        GsonNamingPolicy fieldNamingPolicy = field.getAnnotation(GsonNamingPolicy.class);
        GsonNamingPolicy classNamingPolicy = declaringClass.getAnnotation(GsonNamingPolicy.class);

        FieldNamingPolicy policy = FieldNamingPolicy.IDENTITY;

        if (fieldNamingPolicy != null) {
            policy = fieldNamingPolicy.value();
        } else if (classNamingPolicy != null) {
            policy = classNamingPolicy.value();
        }

        return policy.translateName(field);
    }
}

You can then use this strategy when configuring Gson:

    Gson gson = new GsonBuilder()
            .setFieldNamingStrategy(new AnnotationFieldNamingStrategy())
            .create();

    Data data = gson.fromJson(json, Data.class);

    System.out.println(data.camelCaseData.someField);
    System.out.println(data.snakeCaseData.someField);

This is not exactly what you were asking for, but hopefully it helps you anyway. I don't believe there is a way to check whether a field is annotated at a level beyond the class that declared it. It's possible there is a way to achieve what you are looking for, but it may require working with a custom TypeAdapter or TypeAdapterFactory.

Upvotes: 4

Related Questions