0

I am trying to serialize/deserialize polymorphic objects using gson library. I have tried like this:

A.java

package testing;

public abstract class A {
    private final String id;
    private final String name;

    protected A(String id, String name) {
        this.id = id;
        this.name = name;
    }
}

B.java

package testing;

public class B extends A {
    public B(String id, String name) {
        super(id, name);
    }
}

C.java

package testing;

public class C extends A {
    private String c;

    public C(String id, String name, String cname) {
        super(id, name);
        c = cname;
    }

    public String getC() {
        return c;
    }

    public void setC(String c) {
        this.c = c;
    }
}

Container.java

package testing;

import java.util.ArrayList;
import java.util.List;

public class Container {
    private List<A> list;

    public List<A> getList() {
        return list;
    }


    public Container() {
        list = new ArrayList<>();
        list.add(new B("b_id1", "b_name1"));
        list.add(new C("c_id1", "c_name1", "c_name2"));
    }
}

AListContainer.java

package testing;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public class AListAdapter implements JsonSerializer<List<A>>, JsonDeserializer<List<A>>
{
    private static final String CLASS_NAME = "CLASSNAME";
    private static final String INSTANCE = "INSTANCE";
    private Gson gson = new GsonBuilder()
            .registerTypeAdapter(B.class, new BAdapter())
            .registerTypeAdapter(C.class, new CAdapter())
            .create();

    @Override
    public JsonElement serialize(List<A> listA, Type typeOfSrc,
                                 JsonSerializationContext context) {
        JsonArray array = new JsonArray();
        for (A a : listA) {
            JsonObject obj = new JsonObject();
            String className = a.getClass().getCanonicalName();
            obj.add(CLASS_NAME, context.serialize(className));
            JsonElement elem = gson.toJsonTree(a);
            obj.add(INSTANCE, elem);
            array.add(obj);
        }
        return array;
    }

    @Override
    public List<A> deserialize(JsonElement json,
                                             Type typeOfT,
                                             JsonDeserializationContext context) throws JsonParseException {
        List<A> retVal = new ArrayList();
        JsonArray elems = json.getAsJsonArray();
        for (int i = 0; i < elems.size(); i++) {
            JsonElement oneElem = elems.get(i);
            JsonObject oneObj = oneElem.getAsJsonObject();
            JsonElement elem = oneObj.get(CLASS_NAME);
            String className = elem.getAsString();
            JsonElement metricViolation = oneObj.get(INSTANCE);
            try {
                Class t = Class.forName(className);
                A violation = (A) gson.fromJson(metricViolation, t);
                retVal.add(violation);
            } catch (ClassNotFoundException ex) {
                throw new JsonParseException(ex);
            }
        }
        return retVal;
    }

}

Main.java

package testing;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

import java.util.List;

public class Main {

    public static void main(String [] args) {

        Container d = new Container();
        List<A> listA = d.getList();

        Gson gson =
            new GsonBuilder()
                    .registerTypeAdapter((new TypeToken<List<A>>() {}).getType(), new AListAdapter())
                    .serializeSpecialFloatingPointValues()
                    .create();

        String json = gson.toJson(listA);
        List<A> resultA = gson.fromJson(json, List.class);
        System.out.println(resultA);
    }
}

gson is 2.8.5 version:

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.5</version>
</dependency>

It seems that testing.AListAdapter#serialize and testing.AListAdapter#deserialize are not called. Can you help to understand why?

Vardan Hovhannisyan
  • 1,071
  • 3
  • 16
  • 36
  • Registering specific list adapters does not make much sense here (at least in your current implementation). You have to deal with A, B, and C only, and you can find something very similar here: https://stackoverflow.com/questions/19588020/gson-serialize-a-list-of-polymorphic-objects/22081826#22081826 – terrorrussia-keeps-killing Apr 27 '22 at 09:36
  • Please also avoid `Class.forName(className)` in your deserializer. If the JSON data can be controlled by an adversary, they will be able to load arbitrary classes with this and assign field values for them, which can be exploited. In most use cases the subclasses are known at compile time so there is no need for `Class.forName`; if that is not the case, then at least check that the class name starts with a custom package name, or use `forName(..., false, ...)` and then verify that the class is a subclass of a custom class dedicated to deserialization. – Marcono1234 Apr 29 '22 at 17:44

0 Answers0