Reputation: 801
I currently have the following class:
static final class TabInfo{
public final String tag;
public final Class<?> clss;
public Bundle args;
TabInfo(String _tag, Class<?> _class, Bundle _args) {
tag = _tag;
clss = _class;
args = _args;
}
}
of which I would like to create json out of. In order to do this, I am using the following code:
Gson gson= new Gson();
Type hmType= new TypeToken<TabInfo>(){}.getType();
String json = gson.toJson(methodToGetAnInstanceOfTabInfoClassHere, hmType);
When I am doing this, I get a java.lang.StackOverFlowError:
E/AndroidRuntime(10353): at Java.lang.StringBuffer.append(StringBuffer.java:219)
E/AndroidRuntime(10353): at java.io.StringWriter.write(StringWriter.java:147)
E/AndroidRuntime(10353): at com.google.gson.stream.JsonWriter.string(JsonWriter.java:519)
E/AndroidRuntime(10353): at com.google.gson.stream.JsonWriter.writeDeferredName(JsonWriter.java:387)
E/AndroidRuntime(10353): at com.google.gson.stream.JsonWriter.beginObject(JsonWriter.java:300)
E/AndroidRuntime(10353): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:190)
E/AndroidRuntime(10353): at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrap E/AndroidRuntime(20692): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
E/AndroidRuntime(20692): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
So, my question is: how can I get my gson to create valid json of the java objects from class TabInfo without getting a stackoverflowerror?
Btw. as all of you can see, I have not asked that many questions before, so if you have any feedback for me on how to improve my question: let me know!
EDIT 1: The bundle class is a standard bundle, fi: Bundle args = new Bundle(); args.putint("someint", 1);
See the updated stacktrace...
EDIT 2: If I take a single instance of a TabInfo, by using a String.class as an argument, eg:
TabInfo test= new TabInfo("test", String.class, new Bundle());
String result=gson.toJson(test, hmType);
Then I get the following stacktrace:
E/AndroidRuntime(22068): java.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class: java.lang.String. Forgot to register a type adapter?
E/AndroidRuntime(22068): at com.google.gson.internal.bind.TypeAdapters$1.write(TypeAdapters.java:64)
E/AndroidRuntime(22068): at com.google.gson.internal.bind.TypeAdapters$1.write(TypeAdapters.java:61)
E/AndroidRuntime(22068): at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
E/AndroidRuntime(22068): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
E/AndroidRuntime(22068): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
E/AndroidRuntime(22068): at com.google.gson.Gson.toJson(Gson.java:586)
E/AndroidRuntime(22068): at com.google.gson.Gson.toJson(Gson.java:565)
E/AndroidRuntime(22068): at com.google.gson.Gson.toJson(Gson.java:520)
Does this mean that I need to make a TypeToken for all 24 different classes that might be part of a TabInfo class? How do I resolve this?
Edit 3
Ok, thanks to @Zaske, I have found a fix for the first part of my problem. Using another class signature as shown below
static final class TabInfo{
TabInfo(String _tag, String _class, Bundle _args) {
tag = _tag;
clss = _class;
args = _args;
}
}
Now I can make json out of it, but, when I try to do this by creating the actual HashMap < String, < Stack < TabInfo > > > then again I run into trouble. The typetoken i use in that case is:
Type hmType = new TypeToken<HashMap<String, Stack<TabInfo>>>(){}.getType();
So, now my final question is: how can I convert a set of tabinfo-stacks in a hashmap to json?
Edit 4 Here's some more information: The used Bundle class is the Bundle class used in Android to provide arguments to activities and fragments. (See http://developer.android.com/reference/android/os/Bundle.html)
If I do
TabInfo test= new TabInfo("test", "String", new Bundle());
//stage 1 test
String result=gson.toJson(test);
Log.d("new result=",result);
Then I do get my Json output (see below)
D/new result=(20816): {"args":{"mClassLoader":{"packages":{}},"mMap":{},"mHasFds":false,"mFdsKnown":true,"mAllowFds":true},"clss":"String","tag":"test"}
However, when I try to make a Hashmap of stacks out of the TabInfo classes, then it breaks (it runs out of memory...)...
Upvotes: 3
Views: 13546
Reputation: 739
Another case that occurred to me was I was holding a reference of Dialog in my Serialized class. While writing that class to preferences was throwing an exception.
The fix to avoid that is use of transient
keyword to make a particular field non-serializable (SO link for that particular solution).
Example Code:
public class Player implements Serializable {
private int id;
private String name;
private String slug;
@Nullable
transient Dialog prevDialogWeakRef;//non-serialised object
// set getters()/setters()
}
Upvotes: 0
Reputation: 4137
As I suggested the "change from class" to string,
I allow myself for the sake of our readers to answer here for the first part:
Don't use Class as a field, but use String that will contain the full class name
TabInfo should look like:
static final class TabInfo{
public final String tag;
public final String clss;
public Bundle args;
TabInfo(String _tag, Class<?> _class, Bundle _args) {
tag = _tag;
clss = _class.getName();
args = _args;
}
}
Regarding 2nd part:
I don't know what Bundle class is - please provide info,
as I had to change a bit and write my own class for experiment.
Person class is:
public class Person implements Serializable {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public Person() {
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public void setAge(int age) {
this.age = age;
}
private String name;
private int age;
}
Main class for checking is:
public class Main {
public static void main(String[] args) {
Type type = new TypeToken<HashMap<String,Stack<Person>>>(){}.getType();
Gson gson = new Gson();
HashMap<String,Stack<Person>> map = new HashMap<String, Stack<Person>>();
map.put("yair", new Stack<Person>());
map.get("yair").add(new Person("Yair",36));
String str = gson.toJson(map,type);
System.out.println(str);
map = gson.fromJson(str,type);
String str2 = gson.toJson(map,type);
System.out.println(str2);
}
}
Feel free to run it, you will see both str and str2 are printed just fine.
Update
I checked the Bundle class, and saw that it contains too much information (In my humble opinion) to be a simple arguments collection.
I don't see why in the above question a simple collection cannot be used instead.
Serialization should contain as minimal data as possible
(think about cases in which you take a serialized data and store it on some storage device, or send it over the network).
So unless Bundle provides you a special functionality that a collection doesn't - don't use it.
As a rule of thumb remember you cannot serialize everything with Json - there are limitations (recursive data-types for example) - so yes, there will be cases where you will have to translate from one type to a JSON-serializable type.
This pattern is also used in other cases, I suggest you read more about data transfer objects
Upvotes: 2