Azure / Azure/azure-functions-java-worker

[FeatureRequest] Allow customizing the Gson instance for Json serialization

オープン
#424 コメント 18 件 リアクション 3 件 担当者 1 名 @kaibocai が担当を希望しています GitHub で見る
area:java-functions Enhancement needs-discussion Needs: Attention :wave: New Feature reviewed
主要言語
Java
スター
103
フォーク
74
平均マージ
4日 8時間
マージ済み PR(30日)
2

説明

Hi all,

**Current problem**
The Gson instance used by `RpcUnspecifiedDataTarget` is hardcoded in `RpcJsonDataSource`
https://github.com/Azure/azure-functions-java-worker/blob/3ec27b2b82ba96adef593d51b0bb0e9c0063bfaa/src/main/java/com/microsoft/azure/functions/worker/binding/RpcUnspecifiedDataTarget.java#L68
https://github.com/Azure/azure-functions-java-worker/blob/3ec27b2b82ba96adef593d51b0bb0e9c0063bfaa/src/main/java/com/microsoft/azure/functions/worker/binding/RpcJsonDataSource.java#L20
This means that customizing the way the runtime Json-serializes objects (especially dates) is impossible. See #419 and [this on stackoverflow](https://stackoverflow.com/questions/60688436/spring-cloud-function-azure-json-output-with-datetimes).

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

`AzureGsonConfig.java`:
```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
```grovy
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`:
```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 {

@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 {

@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.

コントリビューションガイド

コントリビューションガイドを開く

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。