0

I have a parent, lets call it Bike that has a name and a color.

public class Bike(){
    String name;
    String color;

}

I have 2 children

public class TrickBike extends Bike(){
    ...
    ...
    public doTrick(){

    }
}

public class FailBike extends Bike(){
   ...
   ...
   public doFail(){

   }
}

These all have appropriate construtor super calls and everything. I now have a garage that holds bikes.

public class Garage(){
     List<Bike> bikes;

}

I add a bunch of bikes, all 3 types(Bike, TrickBike,FailBike). Now, I write this to a JSON String and dump to file with GSON. I then at a later time get the Garage from the JSON file with GSON and I want to try to doTrick() on a TrickBike. I can't cast Bike to TrickBike now because of the Json conversion (this is from trial and error and getting exception in java). So how do I restore this child functionality?

Is there a safe way of doing this? An unsafe way? If I try doTrick() on a FailBike what happens? Can I get the Bike to try and doTrick()?

Thanks.

EDIT: I would like to fix this post-GSON read in. @Chaosfire mentioned copying some GSON source code from the linked question but I would not like to do this for a variety of reasons. Also, I am not reading types TrickBike and Bike or FailBike in. I am reading in the Garage which merely has a List<Bike> so that wouldn't work.

SOLUTION: I migrated over to using jackson and was able to successfully implement this behavior using annotations and @JsonTypeInfo with type labels. So Jackson > Gson for polymorphism.

  • You'll need to add a field identifying the type of bike that also gets written to the json, so you can create the correct subtype when deserializing. – daniu Jun 27 '22 at 16:18
  • Does this answer your question? [How to handle deserializing with polymorphism?](https://stackoverflow.com/questions/15736654/how-to-handle-deserializing-with-polymorphism) – Chaosfire Jun 27 '22 at 16:28
  • @daniu I do believe that a custom deserialization method might be necessary. – Shavk with a Hoon Jun 27 '22 at 16:42
  • @Chaosfire, I can't use that code in my situation so I am looking for an answer that is not fixing the GSON code. Unless that is the only way to do it. – Shavk with a Hoon Jun 27 '22 at 16:42

1 Answers1

0

In my knowledge you would need custom serialization/deserialization. I don't think Gson has functionality add type information to json.

public class BikeTypeAdapter implements JsonSerializer<Bike>, JsonDeserializer<Bike> {

  private final Gson gson = new Gson();
  private final String typeField = "type";

  @Override
  public Bike deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
    String type = ((JsonObject) json).get(this.typeField).getAsString();
    Bike bike;
    try {
      //instantiate with default constructor
      bike = (Bike) Class.forName(type).getConstructor().newInstance();
    } catch (ReflectiveOperationException e) {
      throw new RuntimeException(e);
    }
    bike.setColor(((JsonObject) json).get("color").getAsString());
    bike.setName(((JsonObject) json).get("name").getAsString());
    return bike;
  }

  @Override
  public JsonElement serialize(Bike src, Type typeOfSrc, JsonSerializationContext context) {
    JsonObject object = this.gson.toJsonTree(src).getAsJsonObject();
    object.addProperty(this.typeField, src.getClass().getCanonicalName());
    return object;
  }
}

Somewhat simplified example, which adds property with canonical class name during serialization, and uses it during deserialization to create instance of correct class.

Example usage:

public static void main(String[] args) {
  Garage garage = new Garage();
  garage.bikes.add(new Bike("normal", "red"));
  garage.bikes.add(new TrickBike("trick", "blue"));
  garage.bikes.add(new FailBike("fail", "green"));
  Gson gson = new GsonBuilder()
          .registerTypeAdapter(Bike.class, new BikeTypeAdapter())
          .create();
  String json = gson.toJson(garage);
  System.out.println(json);
  Garage garage1 = gson.fromJson(json, Garage.class);
  System.out.println(garage1);
}
Chaosfire
  • 4,818
  • 4
  • 8
  • 23