cekeriya
cekeriya

Reputation: 306

Configurable @SerializedName in Gson

Could I get @SerializedName value from config file etc.?

I mean like:

@SerializedName(value = configProfider.getJsonFieldName())
private String myField

Thanks.

Upvotes: 2

Views: 1319

Answers (1)

Lyubomyr Shaydariv
Lyubomyr Shaydariv

Reputation: 21115

I was particularly wrong in my comment: this is possible in Gson, but not with @SerializedName (due to how Gson works internally) and non-compile time expressions (due to Java restrictions). Sorry for the confusion. Gson supports a custom field naming strategy that you can supply while configuring your Gson instance.

Consider the following custom annotation:

@Retention(RUNTIME)
@Target(FIELD)
@interface DynamicSerializedName {

    String value();

}

Now just implement your strategy:

final class DynamicSerializedNameStrategy
        implements FieldNamingStrategy {

    private final Function<? super String, String> translator;

    private DynamicSerializedNameStrategy(final Function<? super String, String> translator) {
        this.translator = translator;
    }

    static FieldNamingStrategy getDynamicSerializedNameStrategy(final Function<? super String, String> translator) {
        return new DynamicSerializedNameStrategy(translator);
    }

    @Override
    public String translateName(final Field field) {
        final DynamicSerializedName annotation = field.getAnnotation(DynamicSerializedName.class);
        if ( annotation == null ) {
            // Taking the default naming strategy
            // @SerializedName takes higher priority in ReflectiveTypeAdapterFactory.Adapter anyway
            return FieldNamingPolicy.IDENTITY.translateName(field);
        }
        final String key = annotation.value();
        final String resolvedName = translator.apply(key);
        if ( resolvedName == null ) {
            throw new IllegalArgumentException("Cannot resolve name by " + key + " for " + field);
        }
        return resolvedName;
    }

}

Create an annotation:

final class Model {

    @DynamicSerializedName("gson.model.field")
    final String field = null;

}

How it can be used (system properties-based example):

private static final Gson gson = new GsonBuilder()
        .setFieldNamingStrategy(getDynamicSerializedNameStrategy(System::getProperty))
        .create();

public static void main(final String... args)
        throws IOException {
    try ( final Reader reader = getPackageResourceReader(Q43517297.class, "arbitrary.json") ) {
        final Model model = gson.fromJson(reader, Model.class);
        System.out.println(model.field);
    }
}

This example will fail unless you do:

  • either define the gson.model.field system property programmatically like System.setProperty("gson.model.field", "dynamic");;
  • or define the system property using the -D parameter while starting the JVM: -Dgson.model.field=dynamic.

Consider the following JSON (referenced as arbitrary.json above):

{
    "static": "BY-STATIC-NAME",
    "dynamic": "BY-DYNAMIC-NAME"
}

Once the @DynamicSerializedName annotation is set and the corresponding property is properly configured the output will be:

BY-DYNAMIC-NAME

Once you remove the @DynamicSerializedName annotation from the DTO field, or annotate the field with @SerializedName (it has higher priority as of Gson 2.8.0), the output will be:

BY-STATIC-NAME

Upvotes: 1

Related Questions