我试图将包含接口类型的接口和hashmaps的json字符串反序列化,并使用Gson将包含接口类型的列表反序列化到java对象中。但我得到了
java.lang.RuntimeException:无法为接口com.abc.Dummy调用no-args构造函数。使用Gson为此类型注册InstanceCreator可以解决此问题。
我尝试通过实例化构造函数来为实例创建者注册类型适配器,以实现具有虚拟值的类,但虚拟值不会被反序列化值覆盖?
Dummy是接口,SubClassDummy是实现类。
public class DummyInstanceCreator implements InstanceCreator<Dummy>{
@Override
public SubClassDummy createInstance(Type type) {
return new SubClassDummy("", 2.5, "abc");
}
}
String data = // some json string with interfaces and List<interface.class>, hashmap<int, interface.class>
gsonBuilder.registerTypeAdapter(Dummy.class, new DummyInstanceCreator());
SubClassDummy context = gson.fromJson(data, SubClassDummy.class);
任何人都可以帮忙解决这个问题吗?我无法修改我试图反序列化的java对象。 它是第三方。因此无法添加任何注释或对该类进行任何修改。如何让gson用反序列化的值覆盖虚拟值? 我甚至无法对java对象如何序列化为json字符串进行任何更改。我只是将json字符串用于将其反序列化为特定的java对象。
答案 0 :(得分:0)
java.lang.RuntimeException:无法为接口com.abc.Dummy调用no-args构造函数
您的Dummy
需要是一个类(不是接口)并重新查询默认/空的无参数构造函数:
public class Dummy {
public Dummy (){
}
[...] // your code
}
一些解释:
Json解析器以这种方式创建Dummy
的实例:
Dummy instance = Dummy.class.newInstance(); // calls default constructor
然后将初始化使用反射API instance
。因此,如果您的类没有实现默认构造函数Dummy.class.newInstance()
将失败。
答案 1 :(得分:0)
按照OP的问题,我在下面编写了JUnit测试。它显示了事物在原理上应该如何工作。
但是:由于界面需要使用getter / setter,因此无法立即使用...
问题https://github.com/google/gson/issues/232仍然打开,因此失败...
另请参见Why does GSON use fields and not getters/setters?和https://sites.google.com/site/gson/gson-design-document:“ 我们打算在以后的版本中增强Gson,以支持作为表示Json字段的替代映射的属性。目前,Gson基于字段。”-截至2018年,这仍然是一个意图。
您可以尝试https://stackoverflow.com/a/16400945/1497139
中描述的解决方法对于此用例,最好使用其他JSON库 例如杰克逊(Jackson)或JaxB,例如EclipseLink Moxy。
我为FastJson创建了下面的测试,该测试失败了。
gson的JUnit测试
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依赖项:
<!-- 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>
fastjson的JUnit测试
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依赖项:
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
答案 2 :(得分:0)
要反序列化接口,这对我来说是gson 2.8.5的窍门- 您还需要定义serialize()
public class DummyInstanceDeserializer implements JsonDeserializer<Dummy>,JsonSerializer<Dummy>
{
@Override
public Dummy deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return context.deserialize(json, SubClassDummy.class);
}
@Override
public JsonElement serialize(Dummy src, Type typeOfSrc, JsonSerializationContext context) {
return context.serialize(src);
}
}
...
gsonBuilder.registerTypeAdapter(Dummy.class, new DummyInstanceDeserializer());
要使用默认值,当与DummyInstanceDeserializer一起使用时,这似乎很好用
public class DummyInstanceCreator implements InstanceCreator<**SubClassDummy**>{
@Override
public SubClassDummy createInstance(Type type) {
return new SubClassDummy("", 2.5, "abc");
}
}
...
gsonBuilder.registerTypeAdapter(SubClassDummy.class,new DummyInstanceCreator());