Following the OP's question i wrote the JUnit test below. It shows how things should work in principle.
But: Since the interfaces needs to use getters/setters things will not work out of the box ...
issue https://github.com/google/gson/issues/232 is still open so this fails ...
See also Why does GSON use fields and not getters/setters? and https://sites.google.com/site/gson/gson-design-document: "We intend to enhance Gson in a latter version to support properties as an alternate mapping for indicating Json fields. For now, Gson is fields-based." - it's still an intention as of 2018.
You can try the workarounds described in https://stackoverflow.com/a/16400945/1497139
For this use case you might be better off using a different JSON library
e.g. Jackson, or JaxB e.g EclipseLink Moxy.
For FastJson I created the test below which fails.
JUnit Test for gson
package com.bitplan.rest.jqgrid;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
public class TestGson {
public boolean debug = true;
interface Dummy {
String getName();
void setName(String name);
}
class SubClassDummy implements Dummy {
String name;
/**
* non args constructor to make gson happy
*/
public SubClassDummy() {
}
public SubClassDummy(String pName) {
this.name = pName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class DummyInstanceCreator implements InstanceCreator<Dummy> {
@Override
public Dummy createInstance(Type type) {
return new SubClassDummy();
}
}
class DataContainer {
List<Dummy> names = new ArrayList<Dummy>();
Map<Integer, Dummy> namesByNumber = new HashMap<Integer, Dummy>();
public DataContainer(String... pNames) {
for (String name : pNames) {
SubClassDummy scd = new SubClassDummy(name);
names.add(scd);
namesByNumber.put(names.size(), scd);
}
}
}
@Test
public void testGson() {
GsonBuilder gsonBuilder = new GsonBuilder().setPrettyPrinting();
Gson gson = gsonBuilder.create();
DataContainer dc = new DataContainer("John Doe", "Mary Brown", "Tim Smith");
// some json string with interfaces and
// List<interface.class>, hashmap<int, interface.class>
String data = gson.toJson(dc);
if (debug)
System.out.println(data);
String expected = "{\n" + " \"names\": [\n" + " {\n"
+ " \"name\": \"John Doe\"\n" + " },\n" + " {\n"
+ " \"name\": \"Mary Brown\"\n" + " },\n" + " {\n"
+ " \"name\": \"Tim Smith\"\n" + " }\n" + " ],\n"
+ " \"namesByNumber\": {\n" + " \"1\": {\n"
+ " \"name\": \"John Doe\"\n" + " },\n" + " \"2\": {\n"
+ " \"name\": \"Mary Brown\"\n" + " },\n" + " \"3\": {\n"
+ " \"name\": \"Tim Smith\"\n" + " }\n" + " }\n" + "}";
assertEquals(expected, data);
try {
DataContainer dc2 = gson.fromJson(data, DataContainer.class);
assertNotNull(dc2);
fail("This can't happen - according to https://stackoverflow.com/questions/29739648/deserializing-json-string-containing-interfaces-not-working-in-java-using-gson-2 there should be an exception");
} catch (RuntimeException re) {
assertTrue(re.getMessage().contains("no-args constructor for interface"));
}
gsonBuilder.registerTypeAdapter(Dummy.class, new DummyInstanceCreator());
Gson gson2 = gsonBuilder.setPrettyPrinting().create();
DataContainer dc3 = gson2.fromJson(data, DataContainer.class);
assertNotNull(dc3);
String data2=gson2.toJson(dc3);
// issue https://github.com/google/gson/issues/232 is still open so this fails ...
assertEquals(data,data2);
}
}
Maven dependency used:
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
JUnit Test for fastjson
package com.bitplan.rest.jqgrid;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import com.alibaba.fastjson.JSON;
import com.bitplan.rest.jqgrid.TestGson.DataContainer;
/**
* test Alibaba FastJson
*
* @author wf
*
*/
public class TestJson {
public boolean debug = true;
@Test
public void testJson() {
DataContainer dc = new DataContainer("John Doe", "Mary Brown", "Tim Smith");
// some json string with interfaces and
// List<interface.class>, hashmap<int, interface.class>
// String data = gson.toJson(dc);
String data = JSON.toJSONString(dc);
if (debug)
System.out.println(data);
String expected = "{\n" + " \"names\": [\n" + " {\n"
+ " \"name\": \"John Doe\"\n" + " },\n" + " {\n"
+ " \"name\": \"Mary Brown\"\n" + " },\n" + " {\n"
+ " \"name\": \"Tim Smith\"\n" + " }\n" + " ],\n"
+ " \"namesByNumber\": {\n" + " \"1\": {\n"
+ " \"name\": \"John Doe\"\n" + " },\n" + " \"2\": {\n"
+ " \"name\": \"Mary Brown\"\n" + " },\n" + " \"3\": {\n"
+ " \"name\": \"Tim Smith\"\n" + " }\n" + " }\n" + "}";
assertEquals(expected, data);
DataContainer dc2 = JSON.parseObject(data, DataContainer.class);
assertNotNull(dc2);
DataContainer dc3 = JSON.parseObject(data, DataContainer.class);
assertNotNull(dc3);
String data2 = JSON.toJSONString(dc3);
assertEquals(data, data2);
}
}
Maven dependency used:
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>