Gson未正确序列化LocalDate

aydmsdu9  于 2022-11-06  发布在  其他
关注(0)|答案(2)|浏览(227)

我正在编写一个android应用程序,我想在其中序列化这个Anime.java类的示例。它的超类AnimeBase.java有一个名为aired的字段,它的类型是DateRange。这个DateRange包含两个字段:

public LocalDate from;
public LocalDate to;

序列化非常简单(使用gson),如下所示:

final Gson gson = new Gson();
String data = gson.toJson(obj);

但是,在我的结果中,fromto字段总是空的,如下所示:

// ...
"trailer_url": "https://www.youtube.com/embed/SlNpRThS9t8?enablejsapi\u003d1\u0026wmode\u003dopaque\u0026autoplay\u003d1",
"aired": {
  "from": {}
},
"episodes": 16,
// ...

在这里,to为空,因此它是缺失的(这没关系)。
为什么gson没有序列化这两个LocalDate?它是否与DateRange的setter和getter有关(这有点不寻常,使用OffsetDateTime而不是LocalDate)?
由于这些类来自第三方库,有没有一种好的方法来处理这个问题,而不用复制我自己的应用程序中的所有模型类来序列化/反序列化它们?

ugmeyewa

ugmeyewa1#

看一下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);
  }
}
s4chpxco

s4chpxco2#

我现在可以找到这个问题的根源了。
从Android 9开始,Google添加了一个名为“Restrictions on non-SDK interfaces“的东西,他们在Android dalvik运行时中限制对未公开记录的SDK接口的访问。
由于Gson默认使用ReflectiveTypeAdapterFactory,而ReflectiveTypeAdapterFactory本身在要序列化的对象中查找可序列化字段,因此它严重依赖于反射。
Google已经记录了这种行为,即ReflectiveTypeAdapterFactory使用的函数Class.getDeclaredFields()只返回公共可访问字段,或者更具体地说,只返回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)
    }

}

相关问题