Nir Brachel
Nir Brachel

Reputation: 443

Custom Json serialization with Gson

I am working with a class hierarchy in which one of the classes defines a group of instances

public class Base {
    String id;
    String name;
    String type;
} 

public class Group<T extends Base>  extends Base {
    Collection<T> elements;

}

there are many other types that derive from the base class and can be added to the group. Each type has it's own properties

Gson default serialization works great for non-group objects, but for Group objects I need the collection elements to be serialized with ONLY the Base class properties and exclude all the other properties.

Questions:

  1. What would the best approach to get this done?
  2. What if Group is also a base class, and I want the custom serialization to be done for all its subclasses automatically?

Edit:

@Paul. I think your suggestion would not work in my case. I oversimplified the model.

Here is a more elaborate description of the model.

public interface Node {
    BaseObject getAsBaseObject();
}

public class Base {
    String id;
    String name;
    String type;
}

public class BaseObject extends Base implements Node {
    String description;

    @Override
    public BaseObject getAsBaseObject() {
        return this;
    }
}

public class Group<T extends Node>  extends BaseObject {
    Collection<BaseObject> elements;

    void addElement(T element) {
        elements.add(element.getAsBaseObject());
    }
}

The Node interface is used as a base for composite nodes. Consider Foo and groups of Foo's...

public interface FooBase extends Node {

}

public class Foo extends BaseObject implements FooBase {
    String prop1;
}

public class FooGroup extends Group<FooBase> implements FooBase{

}

So lets say I have a FooGroup containing some Foo's and Foo groups. I need the serialized object to look something like this:

{
  "id": "",
  "type": "",
  "name": "",
  "description": " ",
  "objects": [
    {
      "type": "",
      "name": "",
      "id": ""
    },
    {
      "type": "",
      "name": "",
      "id": ""
    },
    {
      "type": "",
      "name": "",
      "id": ""
    },
    {
      "type": "",
      "name": "",
      "id": ""
    }
  ]
}

I think the @Expose wont work here cause the description field would also be exposed in the objects collection

I was able to do it by registering a custom JsonSerializer but is it the right/simplest approach?

if this is the way to go, is there a way to register only one serializer to handle all the Group class derivatives?

Upvotes: 1

Views: 693

Answers (1)

Paul Benn
Paul Benn

Reputation: 2031

Gson has an @Expose annotation specifically made for this. First, mark the fields of your base class as exposed:

public class Base {
    @Expose
    String id;
    @Expose
    String name;
    @Expose
    String type;
} 

Then, when creating your Gson object, make it ignore fields you haven't exposed:

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

Don't forget to also expose the elements array:

public class Group<T extends Base> extends Base {
    @Expose
    Collection<T> elements;
}

Serialization will work as expected now. To test this, let's create a small helper class which extends Base:

public class BaseExtension extends Base {
    private String notExposed;

    public BaseExtension(String notExposed) {
        this.notExposed = notExposed;
    }
}

Now, lets create a Group of BaseExtensions (using Guava for clarity):

BaseExtension b1 = new BaseExtension("BaseExt1");
BaseExtension b2 = new BaseExtension("BaseExt2");
BaseExtension b3 = new BaseExtension("BaseExt3");

Group group = new Group<>(Lists.newArrayList(b1, b2, b3));

If we serialize this we get three elements without the notExposed property - in fact, the elements will be completely empty, as id, name and type do not have values yet:

System.out.println(gson.toJson(group));
// Produces { "elements" : [ {}, {}, {} ] }

Add values to your Base class to see some more useful output.

Upvotes: 1

Related Questions