Reputation: 2418
I have these classes:
class A implements Composite
{
Composite b = new B();
}
class B implements Composite
{
}
interface Composite
{
}
Basically A
is composed of B
and I want to store them maintaining this composition in a file.
In the Activity I do this:
String filename = "myfile.txt";
A a = new A();
Gson gson = new Gson();
String s = son.toJson(a);
FileOutputStream outputStream;
try
{
outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
outputStream.write(s.getBytes);
outputStream.close();
}
catch(Exception e)
{
}
Then I use this code to read:
FileInputStream fileInputStream = null;
try
{
fileInputStream = openFileInput(filename);
}
catch(FileNotFoundException e)
{}
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream)l;
BufferReader bufferedReader = new BufferedReader(inputStreamReader);
StringBuilder stringBuilder = new StringBuilder();
String line;
try
{
while((line = bufferedReader.readLine()) != null)
{
stringBuilder.append(line);
}
}
catch(IOException e)
{
}
String json = stringBuilder.toString();
Gson gson2 = new Gson();
// Exception here.
A a2 = gson2.fromJson(json, A.class);
The problem is with the object B inside of class A. Gson doesn't seem to know that the type of B
. So I get this exception:
JNI detected error in application: can't make objects of type Composite
Upvotes: 1
Views: 520
Reputation: 14183
The problem is that an interface has no attributes so you need to serialize the class that implements that interface. You need to create the Gson instance with a builder
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Composite.class, new CompositeAdapter());
Gson gson = builder.create();
and define the code to serialize your Composite instances
public static class CompositeAdapter implements JsonSerializer<Composite>, JsonDeserializer<Composite> {
private static final String CLASSNAME = "CLASSNAME";
private static final String DATA = "DATA";
public Composite deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
JsonObject jsonObject = jsonElement.getAsJsonObject();
JsonPrimitive prim = (JsonPrimitive) jsonObject.get(CLASSNAME);
String className = prim.getAsString();
Class klass = null;
try {
klass = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
// TODO: handle somehow
}
return jsonDeserializationContext.deserialize(jsonObject.get(DATA), klass);
}
public JsonElement serialize(Composite jsonElement, Type type, JsonSerializationContext jsonSerializationContext) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty(CLASSNAME, jsonElement.getClass().getName());
jsonObject.add(DATA, jsonSerializationContext.serialize(jsonElement));
return jsonObject;
}
}
basically when it serializes a Composite instance it will also store the class name together with the attributes and when deserializing it will create an instance of the actual class (say B). this way you don't have to worry about creating a serializer and deserializer for each class that implements Composite but if you change the name of the class (the full name, including the package name) it won't be able to deserialize it
Upvotes: 1
Reputation: 686
The cause should be that Gson sees and interface within A, not a class. If i am not mistaken, Gson uses compile time information (i.e. the data type of the variable) and not runtime information (i.e. the variable containing an object of class B).
If you change you classes to the following, it should be serialized just fine:
class A implements Composite
{
B b = new B();
}
class B implements Composite
{
}
interface Composite
{
}
Upvotes: 2