1

I have an abstract class:

public abstract class Measurement {

    String Name;
    String Value;

    abstract void setName(String name);
    abstract Object getName();
    abstract void setValue(String value);
    abstract Object getValue();

    @Override
    abstract public String toString();
}

And several concrete versions such as this one:

public class BasicMeasurement extends Measurement {
       //removed for brevity's sake
}

And finally, a Utility class for serializing to disk for storage

public class Config {
        public List<Measurement> Measurements;
        public HashMap<String,String> Items;

        public Config(){
            Measurements = new ArrayList<Measurement>();
            Items = new HashMap<>();
        }


    }

I am using Gson to (de)serialize this Config class to disk and back. Serializing to disk works fine, but, deserializing explodes due to the abstract class.

So I have start to follow the directions here, when I realized that it will require some significant modification to work in my use case.

Q: What method(s) would be best to (de)serialize my config class that would 1. Not blow up and 2. Would not cause loss of information when deserializing (Even if it doesnt blow up, deserializing to type Measurement would cause loss of the specific concrete values)

Current Save Method

public boolean SaveSettings() {
        try {
            FileOutputStream fileOutputStream = mActivity.openFileOutput(SETTINGS_FILE_NAME, Context.MODE_PRIVATE);
            this.json = gson.toJson(this.Config);
            fileOutputStream.write(this.json.getBytes());
            fileOutputStream.close();
            return true;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

Current Load Method

public static MeasureSettings LoadSettings(Activity activity) throws Exception {
       MeasureSettings measureSettings = new MeasureSettings(activity);
       measureSettings.json = measureSettings.getStringFromFile(SETTINGS_FILE_NAME);
       if(measureSettings.json.length() != 0) {
           measureSettings.Config = gson.fromJson(measureSettings.json, Config.class);
       }
       measureSettings.SaveSettings();
       return measureSettings;
   }
Community
  • 1
  • 1
Wjdavis5
  • 3,952
  • 7
  • 35
  • 63
  • The question linked by you perfectly answers the question. Since GSon can't guess the actual type of the object that has been serialized you MUST attach such information to the serialized element. Indeed what you can do it attach a `String` field which stores the name of the class so that you are able to use that name to deserialize the data (by obtaining the `Class>` through `Class.forName` so that you are then able to pass it to the deserialization context, eg `return context.deserialize(json.get("containedObject"), Class.forName(json.get("objectType")))`) – Jack Jan 02 '15 at 03:49
  • I don't see how having a custom serializer/deserializer would require lots of changes. A serializer/deserializer is implemented and registered, then GSon will use it accordingly without any modification required on your code. – Jack Jan 02 '15 at 03:52
  • @Jack Thank you, I am currently implementing the type to be serialized. Perhaps I misread the code, I was thinking that that example would not work provided the hierarchy I introduce with my Config class. And I haven't been able to get that code snip to execute, something about the Generics Android Studio doesnt like presently. – Wjdavis5 Jan 02 '15 at 03:52
  • You may have better luck with [Jackson](http://wiki.fasterxml.com/JacksonHome) since it is [more configurable](http://jackson.codehaus.org/1.9.9/javadoc/org/codehaus/jackson/annotate/JsonTypeInfo.html) regarding type resolution. – S.D. Jan 02 '15 at 03:59
  • Also - the example at the link is throwing an Overflow for me presently. So I am just working through that. – Wjdavis5 Jan 02 '15 at 04:23

1 Answers1

1

So my assumptions were correct, the added hierarchy from my Config class did bjork the custom serializer/deserializer.

I was however able to work around the problem, and still use the custom deserializer.

DeSerializer - this is only used for the items in my ArrayList

@Override
        public A deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
                throws JsonParseException {
            JsonObject jsonObject = json.getAsJsonObject();
            String type = jsonObject.get("MyType").getAsString();
            try {
                // String thePackage = "com.onetwentyonegwatt.MeasurementLib";
                return context.deserialize(json, Class.forName(type));
            } catch (ClassNotFoundException e) {
                throw new JsonParseException("Unknown element type: " + type, e);
            }
        }

Its usage here:

public static MeasureSettings LoadSettings(Activity activity) throws Exception {
       MeasureSettings measureSettings = new MeasureSettings(activity);
       measureSettings.json = measureSettings.getStringFromFile(SETTINGS_FILE_NAME);
       if(measureSettings.json.length() != 0) {
          JsonParser jsonParser = new JsonParser();
          JsonObject o = (JsonObject)jsonParser.parse(measureSettings.json);
          JsonElement items = o.get("Items");
          measureSettings.Config.Items = gson.fromJson(items,measureSettings.Config.Items.getClass());
          JsonElement measurements = o.get("Measurements");
          for(JsonElement item : measurements.getAsJsonArray()) //Iterating the ArrayList values(As json array) here and then deserializing one by one.
           {
               // deserialize(item,Class.forName(item.getAsJsonObject().get("MyType").getAsString()),context);
               String className = item.getAsJsonObject().get("MyType").getAsString();
               Class<?> type = Class.forName(className);
               Measurement l = (Measurement)measureSettings.gsonBuilder.create().fromJson(item,Class.forName(className));
               measureSettings.Config.Measurements.add(l);
           }
           //measureSettings.Config = measureSettings.gsonBuilder.create().fromJson(measureSettings.json,Config.class);
       }
       measureSettings.SaveSettings();
       return measureSettings;
   }
Wjdavis5
  • 3,952
  • 7
  • 35
  • 63