首页 > 解决方案 > 无法使用 Gson 将对象(使用 Room DB)序列化为 json 字符串

问题描述

我有一个类 Item(见下文),其中我使用了一些 Room db 注释。它还有一个名为 ItemInfo 的嵌套类。这两个类都有一个空的构造函数。

问题是当我尝试序列化 Item 类的对象时,应用程序崩溃并出现以下错误:

E/AndroidRuntime: FATAL EXCEPTION: main
              Process: com.android.carrymates, PID: 18526
              java.lang.SecurityException: Can not make a java.lang.reflect.Method constructor accessible
                  at java.lang.reflect.AccessibleObject.setAccessible0(AccessibleObject.java:133)
                  at java.lang.reflect.AccessibleObject.setAccessible(AccessibleObject.java:119)
                  at com.google.gson.internal.reflect.PreJava9ReflectionAccessor.makeAccessible(PreJava9ReflectionAccessor.java:31)
                  at com.google.gson.internal.ConstructorConstructor.newDefaultConstructor(ConstructorConstructor.java:103)
                  at com.google.gson.internal.ConstructorConstructor.get(ConstructorConstructor.java:85)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:101)
                  at com.google.gson.Gson.getAdapter(Gson.java:458)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:117)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:166)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
                  at com.google.gson.Gson.getAdapter(Gson.java:458)
                  at com.google.gson.internal.bind.ArrayTypeAdapter$1.create(ArrayTypeAdapter.java:48)
                  at com.google.gson.Gson.getAdapter(Gson.java:458)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:117)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:166)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
                  at com.google.gson.Gson.getAdapter(Gson.java:458)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:117)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:166)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
                  at com.google.gson.Gson.getAdapter(Gson.java:458)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:117)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:166)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
                  at com.google.gson.Gson.getAdapter(Gson.java:458)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:117)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:166)
                  at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
                  at com.google.gson.Gson.getAdapter(Gson.java:458)
                  at com.google.gson.Gson.toJson(Gson.java:696)
                  at com.google.gson.Gson.toJson(Gson.java:683)
                  at com.google.gson.Gson.toJson(Gson.java:638)
                  at com.google.gson.Gson.toJson(Gson.java:618)
                  ... more log (irrelevant to question asked)

项目.java

@Entity(tableName = "items", indices = {@Index(value = {"id"}, unique = true), @Index(value = {"owner", "type"})})
public class Item {

    @PrimaryKey
    @NonNull
    String id="";

   //...rest fields are int, boolean and String only

    @Embedded
    ItemInfo itemInfo; // see ItemInfo


    public Item() {

   }


    // ...getters and setters

    @IgnoreExtraProperties
    public static class ItemInfo {

        //...fields are int, boolean and String only

        public ItemInfo() {

        }

        //...getters and setters
    }
}

我的猜测是 Room DB 注释正在添加至少一个java.lang.reflect.MethodGson 无法序列化的类型的对象。

下面是我用来将 Item 对象序列化为 json 字符串的代码,其中是 Item 类的对象,其字段类型为和item的非空值。StringItemInfo

Gson gson = new Gson();
String result = gson.toJson(item); // crash begins from here

我该如何解决这个问题?我希望至少有一个解决方案。

标签: javaandroidgsonandroid-room

解决方案


免责声明:

我不知道RoomDB@Entity类做了什么(尽管看起来 RoomDB 使用子类而不是你编写的类)。

我也在JVM上运行测试

但我可以建议你使用@Expose

public class GsonTest {

  private static class SampleModel {
    @Expose
    private int i;

    private Method method;

    @Expose
    private Nested nested = new Nested();
  }

  private static class Nested {
    @Expose
    private String a = "my string";
  }

  @Test
  public void failsWithMethodField() throws Exception {
    assertThrows(Exception.class, () -> {
      SampleModel sampleModel = new SampleModel();
      sampleModel.i = 10;
      sampleModel.method = Object.class.getDeclaredMethod("equals", Object.class);
      Gson gson = new Gson();
      gson.toJson(sampleModel);
    });
  }

  @Test
  public void withExposedDoesNotFail() {
    assertDoesNotThrow(() -> {
      SampleModel sampleModel = new SampleModel();
      sampleModel.method = Object.class.getDeclaredMethod("equals", Object.class);
      Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
      String json = gson.toJson(sampleModel);
      System.out.println(json); // {"i":0,"nested":{"a":"my string"}}
    });
  }
}

必不可少的部分是Gson使用excludeFieldsWithoutExposeAnnotation选项进行配置:

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

@Expose然后用注解标记所有在序列化和反序列化时应该使用的字段。


推荐阅读