Reputation: 787
I have a class Item (see below) in which I have used some Room db annotations. It also has a nested class named ItemInfo. Both of these classes have an empty constructor.
The problem is that when I try to serialize an object of Item class, app crashes with the following error:
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
}
}
My guess is that Room DB annotations are adding at least one object of type java.lang.reflect.Method
which Gson is unable to serialize.
Below is the code I am using to serialize Item object to json string, where item
is a object of class Item with non-null values of fields of type String
and ItemInfo
.
Gson gson = new Gson();
String result = gson.toJson(item); // crash begins from here
How do I address this problem? I expect at least a workaround solution.
Upvotes: 5
Views: 1800
Reputation: 8297
Disclaimer:
I'm not aware of what RoomDB does with @Entity
classes (though it looks like RoomDB uses subclasses rather than classes written by you).
Also I run the test on JVM
But I can suggest you to use @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"}}
});
}
}
The essential part is to configure Gson
with excludeFieldsWithoutExposeAnnotation
option:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Then mark all fields that should be used when serializing and deserializing with @Expose
annotation.
Upvotes: 1
Reputation: 76689
do not nest these classes, in order to use the @Embedded
annotation:
@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() {
}
...
}
also see this answer, concerning GSON
exclusion strategies (which might be required for itemInfo
)
or simply add these fields directly into class Item
, in order to serialize them all at once -
in order not to add more complexity than required, which only cause issues down the road.
Upvotes: 0
Reputation: 573
normally i get output when i try this,
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);
checkout this : http://tutorials.jenkov.com/java-json/gson.html
Upvotes: 0
Reputation: 760
Try this please: this code snipped is working properly
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
}
}
note the dependencies snipped too please
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';
and the implementation:
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}}
Upvotes: 0
Reputation: 1143
I can suggest you use different objects for different goals (storing in a Room
db and serializing to json
).
You need to have an interface of your Item entity:
public interface Item {
int getId();
//other fields
}
Then you need a specific implementation for a Room
db entity. What you actually already have, but need make same refactoring:
@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
}
Plus you need to get rid of the inner static class ItemInfo and make it in a separate .java
file.
And finally, you need a specific implementation for a Gson
entity:
public class GsonItem implements Item {
private final int id;
public GsonItem(Item origin) {
id = origin.getId();
}
@Override
public int getId() {
return id;
}
}
In this case, you will able to use it like this without any issues:
Gson gson = new Gson();
String result = gson.toJson(new GsonItem(item));
Yes, this approach leads you to write a little bit more code, but lack of unexpected issues like yours definitely cost the effort!
Upvotes: 0