Gson 没有正确序列化 LocalDate
Gson does not correctly serialize LocalDate
我正在编写一个 android 应用程序,我想在其中序列化此 Anime.java
class. Its superclass AnimeBase.java
has a field called aired
, which is of the type DateRange
的实例。此 DateRange
包含两个字段:
public LocalDate from;
public LocalDate to;
序列化非常简单(使用 gson),如下所示:
final Gson gson = new Gson();
String data = gson.toJson(obj);
但是,在我的结果中,from
和 to
字段总是空的,如下所示:
// ...
"trailer_url": "https://www.youtube.com/embed/SlNpRThS9t8?enablejsapi\u003d1\u0026wmode\u003dopaque\u0026autoplay\u003d1",
"aired": {
"from": {}
},
"episodes": 16,
// ...
在这里,to
为空,因此它丢失了(没关系)。
为什么gson不序列化这两个LocalDate
?它与 DateRange
s setter 和 getter 有什么关系吗(这有点不寻常,用 OffsetDateTime
而不是 LocalDate
)?
由于这些 类 来自第 3 方库,我有没有一种好的方法来处理这个问题,而无需在我自己的 serializing/deserializing 应用程序中复制所有模型 类他们?
看看https://github.com/gkopff/gson-javatime-serialisers
LocalDate 对象有序列化程序。
如果您选择创建自己的序列化器:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(new TypeToken<LocalDate>(){}.getType(), new LocalDateConverter());
Gson gson = builder.create();
...
public class LocalDateConverter implements JsonSerializer<LocalDate>, JsonDeserializer<LocalDate> {
public JsonElement serialize(LocalDate src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(DateTimeFormatter.ISO_LOCAL_DATE.format(src));
}
public LocalDate deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return DateTimeFormatter.ISO_LOCAL_DATE.parse(json.getAsString(), LocalDate::from);
}
}
我现在可以找到这个问题的根源了。
从 Android 9 开始,Google 添加了一个名为“Restrictions on non-SDK interfaces”的东西,它们限制对 [=33] 中未 public 记录的 SDK 接口的访问=] dalvik 运行时。
由于 Gson 默认使用 ReflectiveTypeAdapterFactory
,它本身在要序列化的对象中查找可序列化字段,因此它在很大程度上依赖于反射。
Google 记录了这种行为,ReflectiveTypeAdapterFactory
使用的函数 Class.getDeclaredFields()
仅 return 可访问字段或更多 public具体的,只有被 Google 列入白名单的字段。
https://developer.android.com/guide/app-compatibility/restrictions-non-sdk-interfaces#results-of-keeping-non-sdk
在参考文档中,Google 明确将 java.time.LocalDate 字段列为灰名单:
Ljava/time/LocalDate;->day:S,greylist-max-o
我不确定为什么这个访问在发布模式下仍然有效,并且只有在构建 debuggable
时才会出现这种行为,但我想这将在未来被删除 Android版本也是。
因此,我们添加了我们自己的向后兼容的序列化程序(类似于 @k1r0 的序列化程序,但仍然适用于以前序列化的值):
class LocalDateJsonSerializer : JsonSerializer<LocalDate>, JsonDeserializer<LocalDate> {
override fun serialize(src: LocalDate, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
return JsonObject().also {
it.addProperty("year", src.year)
it.addProperty("month", src.monthValue)
it.addProperty("day", src.dayOfMonth)
}
}
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): LocalDate {
val jsonObject = json.asJsonObject
return LocalDate.of(jsonObject["year"].asInt, jsonObject["month"].asInt, jsonObject["day"].asInt)
}
}
我正在编写一个 android 应用程序,我想在其中序列化此 Anime.java
class. Its superclass AnimeBase.java
has a field called aired
, which is of the type DateRange
的实例。此 DateRange
包含两个字段:
public LocalDate from;
public LocalDate to;
序列化非常简单(使用 gson),如下所示:
final Gson gson = new Gson();
String data = gson.toJson(obj);
但是,在我的结果中,from
和 to
字段总是空的,如下所示:
// ...
"trailer_url": "https://www.youtube.com/embed/SlNpRThS9t8?enablejsapi\u003d1\u0026wmode\u003dopaque\u0026autoplay\u003d1",
"aired": {
"from": {}
},
"episodes": 16,
// ...
在这里,to
为空,因此它丢失了(没关系)。
为什么gson不序列化这两个LocalDate
?它与 DateRange
s setter 和 getter 有什么关系吗(这有点不寻常,用 OffsetDateTime
而不是 LocalDate
)?
由于这些 类 来自第 3 方库,我有没有一种好的方法来处理这个问题,而无需在我自己的 serializing/deserializing 应用程序中复制所有模型 类他们?
看看https://github.com/gkopff/gson-javatime-serialisers LocalDate 对象有序列化程序。
如果您选择创建自己的序列化器:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(new TypeToken<LocalDate>(){}.getType(), new LocalDateConverter());
Gson gson = builder.create();
...
public class LocalDateConverter implements JsonSerializer<LocalDate>, JsonDeserializer<LocalDate> {
public JsonElement serialize(LocalDate src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(DateTimeFormatter.ISO_LOCAL_DATE.format(src));
}
public LocalDate deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return DateTimeFormatter.ISO_LOCAL_DATE.parse(json.getAsString(), LocalDate::from);
}
}
我现在可以找到这个问题的根源了。
从 Android 9 开始,Google 添加了一个名为“Restrictions on non-SDK interfaces”的东西,它们限制对 [=33] 中未 public 记录的 SDK 接口的访问=] dalvik 运行时。
由于 Gson 默认使用 ReflectiveTypeAdapterFactory
,它本身在要序列化的对象中查找可序列化字段,因此它在很大程度上依赖于反射。
Google 记录了这种行为,ReflectiveTypeAdapterFactory
使用的函数 Class.getDeclaredFields()
仅 return 可访问字段或更多 public具体的,只有被 Google 列入白名单的字段。
https://developer.android.com/guide/app-compatibility/restrictions-non-sdk-interfaces#results-of-keeping-non-sdk
在参考文档中,Google 明确将 java.time.LocalDate 字段列为灰名单:
Ljava/time/LocalDate;->day:S,greylist-max-o
我不确定为什么这个访问在发布模式下仍然有效,并且只有在构建 debuggable
时才会出现这种行为,但我想这将在未来被删除 Android版本也是。
因此,我们添加了我们自己的向后兼容的序列化程序(类似于 @k1r0 的序列化程序,但仍然适用于以前序列化的值):
class LocalDateJsonSerializer : JsonSerializer<LocalDate>, JsonDeserializer<LocalDate> {
override fun serialize(src: LocalDate, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
return JsonObject().also {
it.addProperty("year", src.year)
it.addProperty("month", src.monthValue)
it.addProperty("day", src.dayOfMonth)
}
}
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): LocalDate {
val jsonObject = json.asJsonObject
return LocalDate.of(jsonObject["year"].asInt, jsonObject["month"].asInt, jsonObject["day"].asInt)
}
}