google / gson

A Java serialization/deserialization library to convert Java Objects into JSON and back
Apache License 2.0
23.13k stars 4.27k forks source link

Serialization failure at index 0 and 1 in camel case field naming convention #2667

Closed corock closed 3 months ago

corock commented 3 months ago

Gson version

2.9.1

Java / Android version

Java 11

Used tools

Description

When calling the specific API method named foo, there is an issue with serializing the returned type FooResponse. The problem occurs when applying naming conventions immediately to the first and second words. This phenomenon does not occur at index 2 or higher, but it specifically occurs at index 0 and 1.

Upon further investigation, I found that when using GsonBuilder, you can setFieldNamingStrategy() or setFieldNamingPolicy() through method chaining. It would be helpful if you could provide some ideas on whether this method can be used to solve the issue described below.

Expected behavior

{
  "data": [
    {
      // (...)
      "xAndYType": "Difference",
      "xAndYBaseOrderQuantity": 1,
    },
  ],
  // (...)
}

Actual behavior

{
  "data": [
    {
      // (...)
      "xandYType": "Difference",
      "xandYBaseOrderQuantity": 1,
    },
  ],
  // (...)
}

Reproduction steps

Precondition

  1. Unable to modify the API response specification that is internally called (e.g. provided property names)

Operation Steps

  1. Set up a simple Spring Boot application environment (Spring Boot v2.7.14).
  2. Add the Retrofit-related dependencies in the build.gradle file for HTTP client calls
  3. Register the HTTP client configuration class and Spring beans.
  4. Create a simple API for a GET request and return a custom response format called FooResponse. When naming the properties, name them in camel case directly for index 0 and 1, such as xandYType(When serialization occurs, it should appear as xAnd... in the JSON response value).

build.gradle

dependencies {
    // (...)
    implementation group: 'com.squareup.retrofit2', name: 'retrofit', version: '2.5.0'
    implementation group: 'com.squareup.retrofit2', name: 'converter-gson', version: '2.4.0'
    // (...)
}

FooConfig.java

public class FooConfig {

  @Bean
  public Retrofit fooRetrofit(OkHttpClient fooClient) {
    Gson gson = new GsonBuilder().registerTypeAdapter(LocalDateTime.class,
            (JsonDeserializer<Object>) (json, typeOfT, context) -> LocalDateTime.parse(
                json.getAsString(), DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")))
        .setPrettyPrinting().create();

    return new Retrofit.Builder()
        .baseUrl("http://mapped-by-any-hostname.com")
        .client(displayClient)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build();
  }

  @Bean
  public OkHttpClient fooClient() {
    return new OkHttpClient.Builder()
        .addInterceptor(new AuthorizationInterceptor())
        .connectionPool(RestClientConfiguration.defaultConnectionPool())
        .connectTimeout(3, TimeUnit.SECONDS)
        .readTimeout(10, TimeUnit.SECONDS)
        .writeTimeout(10, TimeUnit.SECONDS)
        .build();
  }

}

Exception stack trace

It is not an exception, but the lies with proper capitalization mapping. The client-side cannot receive the appropriate response value due to this issue.

corock commented 3 months ago

I will resolve it myself and close the issue.

Marcono1234 commented 3 months ago

I found that when using GsonBuilder, you can setFieldNamingStrategy() or setFieldNamingPolicy() through method chaining

Alternatively you can annotate the fields with @SerializedName. That might be easier if you just want to change the name of a few fields.