jmhobbs

Gson Inheritance Issues

So, work has me writing Java for the first time in my life. I needed to handle some data in JSON format, and was using Jackson and it's ObjectMapper for a while. Fast and a relatively nice interface.

Today I switched to Gson for the even simpler API. It's slower, but I'm just munging little strings every once in a while, so that's not a problem.

The one thing I came across today that didn't go as expected was serialization with inheritance in my objects. Here's where I started:

package org.velvetcache.java.toys;

import com.google.gson.Gson;

public class GsonToy {
  
  public static void main(String[] args) {
    Gson gson = new Gson();

    Core core = new Core();
    System.out.println( gson.toJson( core ) );
    
    Inherits inherits = new Inherits();
    System.out.println( gson.toJson( inherits ) );
  }

  public static class Core {
    public int inheritanceDepth = 1;
    public String className = "Core";
  }

  public static class Inherits extends Core {
    public int inheritanceDepth = 2;
    public String className = "Inherits";
  }

}

I expected to see the JSON representing of each. Much to my surprise, I got this instead:

{"inheritanceDepth":1,"className":"Core"}
{"inheritanceDepth":1,"className":"Core"}

What the what? Weird. So I thought I'd try adding a constructor and setting it there.

  public static class Inherits extends Core {
    public int inheritanceDepth = 2;
    public String className = "Inherits";
    
    public Inherits () {
      inheritanceDepth = 2;
      className = "Inherits";
    }
  }

Again, no luck! I got the same output as before. I gave it one last shot.

  public static class Inherits extends Core {    
    public Inherits () {
      inheritanceDepth = 2;
      className = "Inherits";
    }
  }

That time I got it.

{"inheritanceDepth":1,"className":"Core"}
{"inheritanceDepth":2,"className":"Inherits"}

Strange. So it seems that when serializing an object, Gson looks for the value of a field in the superclass. However, when you are in the object you are accessing the local field. It seems like a weird scope thing. Interestingly, if you provide a new implementation at the subclass level it will use that one instead.

This seems to be noted in the user guide at "Finer Points with Objects". I must have just missed it.

Fields corresponding to the outer classes in inner classes, anonymous classes, and local classes are ignored and not included in serialization or deserialization

Easy enough, though a little annoying.