Skip to content

[FeatureRequest] Allow customizing the Gson instance for Json serialization #424

Open
@CrawX

Description

@CrawX

Hi all,

Current problem
The Gson instance used by RpcUnspecifiedDataTarget is hardcoded in RpcJsonDataSource



This means that customizing the way the runtime Json-serializes objects (especially dates) is impossible. See #419 and this on stackoverflow.

Workaround
I came up with a dirty workaround to replace the used Gson instance with the spring-provided one via Reflection:

AzureGsonConfig.java:

@Configuration
@ConditionalOnClass(RpcJsonDataSource.class)
public class AzureGsonConfig {

  @SneakyThrows
  public AzureGsonConfig(Gson gson) {
    final Field gsonField = RpcJsonDataSource.class.getField("gson");
    gsonField.setAccessible(true);
    final Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(gsonField, gsonField.getModifiers() & ~Modifier.FINAL);

    gsonField.set(null, gson);
  }
}

This requires a compile-time dependency like this

compileOnly files('libs/azure-functions-java-worker.jar')

(referencing the runtime as a jar file because I couldn't find it in maven central).

Having replaced the Gson instance means you can have a second configuration that customizes the date serialization behaviour of the one from spring.

GsonConfig.java:

@Configuration
public class GsonConfig {

  @Bean
  GsonBuilderCustomizer dateFormatGsonBuilderCustomizer() {
    return gsonBuilder -> {
      gsonBuilder.registerTypeAdapter(
          LocalDate.class, new LocalDateAdapter()
      );
      gsonBuilder.registerTypeAdapter(
          LocalDateTime.class, new LocalDateTimeAdapter()
      );
    };
  }

  static class LocalDateAdapter extends TypeAdapter<LocalDate> {

    @Override
    public void write(final JsonWriter jsonWriter, final LocalDate localDate) throws IOException {
      if (localDate == null) {
        jsonWriter.nullValue();
      } else {
        jsonWriter.value(localDate.toString());
      }
    }

    @Override
    public LocalDate read(final JsonReader jsonReader) throws IOException {
      if (jsonReader.peek() == JsonToken.NULL) {
        jsonReader.nextNull();
        return null;
      } else {
        return LocalDate.parse(jsonReader.nextString());
      }
    }
  }

  static class LocalDateTimeAdapter extends TypeAdapter<LocalDateTime> {

    @Override
    public void write(final JsonWriter jsonWriter, final LocalDateTime localDateTime) throws IOException {
      if (localDateTime == null) {
        jsonWriter.nullValue();
      } else {
        jsonWriter.value(localDateTime.toString());
      }
    }

    @Override
    public LocalDateTime read(final JsonReader jsonReader) throws IOException {
      if (jsonReader.peek() == JsonToken.NULL) {
        jsonReader.nextNull();
        return null;
      } else {
        return LocalDateTime.parse(jsonReader.nextString());
      }
    }
  }
}

While this works, it's not very clean and can break easily.

Alternative
Serialize the objects to strings in the functions or handlers and return them as such. Personally, I don't like this approach.

Feature request
Somehow allow injecting or customizing the Gson instance used in the azure-functions-worker runtime.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions