Не удалось сериализовать объект (используя Room DB) в строку json, используя Gson - PullRequest
0 голосов
/ 05 сентября 2018

У меня есть класс 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)

Item.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.Method, который Gson не может сериализовать.

Ниже приведен код, который я использую для сериализации объекта Item в строку json, где item - это объект класса Item с ненулевыми значениями полей типа String и ItemInfo.

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

Как мне решить эту проблему? Я ожидаю хотя бы обходного решения.

Ответы [ 5 ]

0 голосов
/ 11 сентября 2018

Отказ от ответственности:

Я не знаю, что 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 аннотации.

0 голосов
/ 11 сентября 2018

не вкладывайте эти классы, чтобы использовать аннотацию @Embedded:

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

    @PrimaryKey
    @ColumnInfo(name = "id")
    String id = null;

    @Embedded
    ItemInfo itemInfo;

    public Item() {

    }
}

@Entity(tableName = "item_info")
public class ItemInfo {

    public ItemInfo() {

    }

    ...
}

также см. Этот ответ , касающийся GSON стратегий исключения (которые могут потребоваться для itemInfo)

или просто добавьте эти поля непосредственно в класс Item, чтобы сериализовать их все сразу -

, чтобы не добавлять больше сложности, чем требуется, что только вызывает проблемы в будущем.

0 голосов
/ 11 сентября 2018

Я могу предложить вам использовать разные объекты для разных целей (сохранение в Room дБ и сериализация в json).

Вам необходим интерфейс вашей Item сущности:

public interface Item {

    int getId();

    //other fields
}

Тогда вам нужна конкретная реализация для сущности Room db. То, что у вас уже есть, но нужно сделать тот же рефакторинг:

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

    @PrimaryKey
    @NonNull
    private int id;

    //other fields

    public RoomItem() {
    }

    public RoomItem(Item item) {
        id = item.getId();
    }

    @Override
    public int getId() {
        return 0;
    }

    @Override
    public void setId(int id) {
        this.id = id;
    }

    //other getters and setters
}

Кроме того, вам нужно избавиться от внутреннего статического класса ItemInfo и сделать его в отдельном .java файле.

И, наконец, вам нужна конкретная реализация для Gson сущности:

public class GsonItem implements Item {

    private final int id;

    public GsonItem(Item origin) {
        id = origin.getId();
    }

    @Override
    public int getId() {
        return id;
    }
}

В этом случае вы сможете использовать его без проблем:

Gson gson = new Gson();
String result = gson.toJson(new GsonItem(item));

Да, этот подход заставляет вас написать немного больше кода, но отсутствие неожиданных проблем, подобных вашей, определенно стоит усилий!

0 голосов
/ 11 сентября 2018

Попробуйте, пожалуйста: этот код работает правильно

import android.arch.persistence.room.Embedded;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.Index;
import android.arch.persistence.room.PrimaryKey;
import android.support.annotation.NonNull;

import com.google.firebase.database.IgnoreExtraProperties;

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

        }

        int prop1;
        String id="";
        //...getters and setters
    }
}

обратите внимание, что зависимости отсекаются тоже, пожалуйста

implementation 'com.google.firebase:firebase-core:16.0.3'
implementation "com.google.firebase:firebase-database:16.0.1"

// Arch
implementation "android.arch.core:runtime:1.1.1"
implementation "android.arch.core:common:1.1.1"

implementation 'android.arch.persistence.room:runtime:1.1.1';
annotationProcessor 'android.arch.persistence.room:compiler:1.1.1';

и реализация:

        Item item = new Item();

        item.id = "Rover";
        item.itemInfo = new Item.ItemInfo();
        item.itemInfo.id = "asd";
        item.itemInfo.prop1 = 1;

        Gson gson = new Gson();

        String json = gson.toJson(item); // here json ={"id":"Rover","itemInfo":{"id":"asd","prop1":1}}
0 голосов
/ 11 сентября 2018

обычно я получаю вывод, когда пытаюсь это сделать,

public class Car { 

    public String brand = null;

    public int    doors = 0;

}


Car car = new Car();

car.brand = "Rover";

car.doors = 5;

Gson gson = new Gson();

String json = gson.toJson(car);

оформить заказ: http://tutorials.jenkov.com/java-json/gson.html

...