在项目中同样遇到了对json字符串进行反序列化时,遇到了多态情况下,无法找到对应类,所以写这篇文章来mark一下
首先抛出原始代码,再给上解决方案~
原始代码:
原始json串:
{"type":"int","specs":{"min":"1","max":"12","unit":"aw","unitName":"饱和度","step":"1"}}
TslDataType对象,Type 为内部枚举类,TslSpecs为接口,对应枚举类类型的各个实现类
TslDataType:构造方法中type是通过tslSpecs的方法来给赋值的
import com.lbs.iot.beans.tsl.specs.TslSpecs;
public class TslDataType {
private final Type type;
private final TslSpecs specs;
public TslDataType(TslSpecs specs) {
this.type = specs.getType();
this.specs = specs;
}
public Type getType() {
return type;
}
public TslSpecs getSpecs() {
return specs;
}
public enum Type {
INT("int"),
TEXT("text"),
DATE("date"),
BOOL("bool"),
ENUM("enum"),
ARRAY("array"),
FLOAT("float"),
STRUCT("struct"),
DOUBLE("double");
private final String type;
Type(String type) {
this.type = type;
}
public String getType() {
return type;
}
}
}
TslSpecs接口:
import com.lbs.iot.beans.tsl.schema.TslDataType;
public interface TslSpecs {
TslDataType.Type getType();
}
并附上两个实现类代码:IntSpecs和TexSpecs
import com.lbs.iot.beans.tsl.schema.TslDataType;
public class IntSpecs implements TslSpecs {
private final int min;
private final int max;
private final int step;
public IntSpecs(int min, int max, int step) {
this.min = min;
this.max = max;
this.step = step;
}
public IntSpecs() {
this(Integer.MIN_VALUE, Integer.MAX_VALUE, 1);
}
public int getMin() {
return min;
}
public int getMax() {
return max;
}
public int getStep() {
return step;
}
@Override
public TslDataType.Type getType() {
return TslDataType.Type.INT;
}
}
import com.lbs.iot.beans.tsl.schema.TslDataType;
public class TextSpecs implements TslSpecs {
private final int length;
public TextSpecs(int length) {
this.length = length;
}
public TextSpecs() {
this(2048);
}
public int getLength() {
return length;
}
@Override
public TslDataType.Type getType() {
return TslDataType.Type.TEXT;
}
}
这种情况下,直接通过Gson的构建对象来反序列化是无法成功的,
会报错Unable to invoke no-args constructor for interface com.lbs.iot.beans.tsl.specs.TslSpecs,Registering an InstanceCreator with Gson for this type may fix this problem.
原因:这是因为反序列化的时候,遇到了TslSpecs接口,无法确定是那个实现类,所以无法实例化,所以才出现了上面的问题,
那么解决的办法就是,自定义反序列化,遇到此接口时能找到对应的实现类。
解决办法:
第一步,自定义TslDeserializer 类实现 JsonDeserializer<TslDataType>接口,并传入对象泛型。类里包含了构造方法,自定义注册方法,自定义反序列化处理方法
在deserialize方法中就能获取到对象对应的json块,然后根据json串中的type值就可以获取到提前注册到map中的对应的实现类(第二步构建Gson的时候会明白);
import com.google.gson.*;
import com.lbs.iot.beans.tsl.schema.TslDataType;
import com.lbs.iot.beans.tsl.specs.TslSpecs;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
public class TslDeserializer implements JsonDeserializer<TslDataType>
{
String tslTypeElementName;
Gson gson;
Map<String, Class<? extends TslSpecs>> tslTypeRegistry;
public TslDeserializer(String tslTypeElementName)
{
this.tslTypeElementName = tslTypeElementName;
gson = new Gson();
tslTypeRegistry = new HashMap<>(); // Java 7 required for this syntax.
}
public void registerSpecsType(String tslTypeName, Class<? extends TslSpecs> tslSpecs)
{
tslTypeRegistry.put(tslTypeName, tslSpecs);
}
@Override
public TslDataType deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException
{
JsonObject tslDataObject = json.getAsJsonObject();
JsonElement tslTypeElement = tslDataObject.get(tslTypeElementName);
Class<? extends TslSpecs> tslSpecs = tslTypeRegistry.get(tslTypeElement.getAsString());
TslSpecs specs = gson.fromJson(tslDataObject.get("specs"), tslSpecs);
TslDataType dataType = new TslDataType(specs);
return dataType;
}
}
第二步,根据自定义TslDeserializer类,来构建Gson对象,
此处需要注意:原始json串中的type值是关键,反序列化时是根据type值来与 tslDeserializer.registerSpecsType中的key来对应的,所以进行接口反序列化的时候,必定要有一个通用的key来进行区分,否则无法进行实现类的对应匹配,也就无法完成实例化
TslDeserializer tslDeserializer = new TslDeserializer("type");此行代码就是指定了根据Json串中那个字段来获取key,然后通过key值从自定义注册器中获取对应的class类进行实例化,然后再通过gosn的反序列化,即可实例化对象,然后返回对象即可~
//先创建对象,再根据type值进行注册,并映射对应的实现类
TslDeserializer tslDeserializer = new TslDeserializer("type");
tslDeserializer.registerSpecsType("int", IntSpecs.class);
tslDeserializer.registerSpecsType("text", TextSpecs.class);
tslDeserializer.registerSpecsType("date", DateSpecs.class);
tslDeserializer.registerSpecsType("bool", BoolSpecs.class);
tslDeserializer.registerSpecsType("enum", EnumSpecs.class);
tslDeserializer.registerSpecsType("array", ArraySpecs.class);
tslDeserializer.registerSpecsType("float", FloatSpecs.class);
tslDeserializer.registerSpecsType("struct", StructSpecs.class);
tslDeserializer.registerSpecsType("double", DoubleSpecs.class);
//将tslDeserializer注册进TypeAdapter中去,遇到TslDataType.class时,就走自定义方法中
Gson gson = new GsonBuilder()
.serializeNulls()
.registerTypeAdapter(TslDataType.class, tslDeserializer)
.create();
第三步:直接 通过上面获取到的gosn对象,进行反序列化获取对象即可
TslDataType tslDataType = gson.fromJson(jsonData, TslDataType.class);
至此,基于Gson的接口反序列化也实现了。