fastjson扩展——多态支持
fastjson在从json字符串解析成对象的时候是不支持多态的。
什么意思呢?就是说我调用JSON.parseObject(jsonstr, XXX.class)方法的时候,如果XXX是一个抽象类,或者接口,我是没有办法根据json字符串中提供的实现类信息解析成相应的实现类的。这样调用后的结果是null。
如何做到对多态的支持呢?我们先要研究一下fastjson的源码。我做的扩展是基于最新版本fastjson-1.1.28.jar进行的。
源码分析
解析的类从JSON转交给DefaultJSONParser
我们调用JSON.parseObject(jsonstr, XXX.class)方法的时候,最后会调用一个parseObject(String input, Type clazz, ParserConfig config, int featureValues, Feature... features)方法。在这个方法中,关键是实例化了一个DefaultJSONParser实例,这个是解析的关键。调用的是parser.parseObject(clazz)。
- public static final <T> T parseObject(String text, Class<T> clazz) {
- //feature可以new Feature[0]这样生成
- return parseObject(text, clazz, new Feature[0]);
- }
- public static final <T> T parseObject(String text, Class<T> clazz,
- Feature... features) {
- //最终调用的是该方法
- return (T) parseObject(text, (Type) clazz,
- ParserConfig.getGlobalInstance(), DEFAULT_PARSER_FEATURE,
- features);
- }
- public static final <T> T parseObject(String input, Type clazz,
- ParserConfig config, int featureValues, Feature... features) {
- if (input == null) {
- return null;
- }
- for (Feature featrue : features) {
- featureValues = Feature.config(featureValues, featrue, true);
- }
- //解析工作转交给DefaultJSONParser
- DefaultJSONParser parser = new DefaultJSONParser(input, config,
- featureValues);
- T value = (T) parser.parseObject(clazz);
- handleResovleTask(parser, value);
- parser.close();
- return (T) value;
- }
解析的真正工作者,各种Deserializer
在DefaultJSONParser的parseObject中,通过ParserConfig去获得相应的反序列化类,并通过反序列化类去反序列化出我们需要解析的对象。
- public <T> T parseObject(Type type) {
- if (lexer.token() == JSONToken.NULL) {
- lexer.nextToken();
- return null;
- }
- //通过ParserConfig去获得反序列化工具类
- ObjectDeserializer derializer = config.getDeserializer(type);
- try {
- //通过反序列化工具类解析出我们需要的对象
- return (T) derializer.deserialze(this, type, null);
- } catch (JSONException e) {
- throw e;
- } catch (Throwable e) {
- throw new JSONException(e.getMessage(), e);
- }
- }
ParserConfig是如何管理Deserializer的
常用类的Deserializer保存在IdentityHashMap之中
我们先从ParserConfig的构造器看起,其中向类型为HashSet<Class<?>>的primitiveClasses成员变量添加了各种常用类。向类型为IdentityHashMap<Type, ObjectDeserializer>的derializers成员变量绑定常用类的Deserializer。
- private final Set<Class<?>> primitiveClasses = new HashSet<Class<?>>();
- private final IdentityHashMap<Type, ObjectDeserializer> derializers = new IdentityHashMap<Type, ObjectDeserializer>();
- public ParserConfig(){
- primitiveClasses.add(boolean.class);
- primitiveClasses.add(Boolean.class);
- primitiveClasses.add(char.class);
- primitiveClasses.add(Character.class);
- primitiveClasses.add(byte.class);
- primitiveClasses.add(Byte.class);
- primitiveClasses.add(short.class);
- primitiveClasses.add(Short.class);
- primitiveClasses.add(int.class);
- primitiveClasses.add(Integer.class);
- primitiveClasses.add(long.class);
- primitiveClasses.add(Long.class);
- primitiveClasses.add(float.class);
- primitiveClasses.add(Float.class);
- primitiveClasses.add(double.class);
- primitiveClasses.add(Double.class);
- primitiveClasses.add(BigInteger.class);
- primitiveClasses.add(BigDecimal.class);
- primitiveClasses.add(String.class);
- primitiveClasses.add(java.util.Date.class);
- primitiveClasses.add(java.sql.Date.class);
- primitiveClasses.add(java.sql.Time.class);
- primitiveClasses.add(java.sql.Timestamp.class);
- derializers.put(SimpleDateFormat.class, DateFormatDeserializer.instance);
- derializers.put(java.sql.Timestamp.class, TimestampDeserializer.instance);
- derializers.put(java.sql.Date.class, SqlDateDeserializer.instance);
- derializers.put(java.sql.Time.class, TimeDeserializer.instance);
- derializers.put(java.util.Date.class, DateDeserializer.instance);
- derializers.put(Calendar.class, CalendarDeserializer.instance);
- derializers.put(JSONObject.class, JSONObjectDeserializer.instance);
- derializers.put(JSONArray.class, JSONArrayDeserializer.instance);
- derializers.put(Map.class, MapDeserializer.instance);
- derializers.put(HashMap.class, MapDeserializer.instance);
- derializers.put(LinkedHashMap.class, MapDeserializer.instance);
- derializers.put(TreeMap.class, MapDeserializer.instance);
- derializers.put(ConcurrentMap.class, MapDeserializer.instance);
- derializers.put(ConcurrentHashMap.class, MapDeserializer.instance);
- derializers.put(Collection.class, CollectionDeserializer.instance);
- derializers.put(List.class, CollectionDeserializer.instance);
- derializers.put(ArrayList.class, CollectionDeserializer.instance);
- derializers.put(Object.class, JavaObjectDeserializer.instance);
- derializers.put(String.class, StringDeserializer.instance);
- derializers.put(char.class, CharacterDeserializer.instance);
- derializers.put(Character.class, CharacterDeserializer.instance);
- derializers.put(byte.class, NumberDeserializer.instance);
- derializers.put(Byte.class, NumberDeserializer.instance);
- derializers.put(short.class, NumberDeserializer.instance);
- derializers.put(Short.class, NumberDeserializer.instance);
- derializers.put(int.class, IntegerDeserializer.instance);
- derializers.put(Integer.class, IntegerDeserializer.instance);
- derializers.put(long.class, LongDeserializer.instance);
- derializers.put(Long.class, LongDeserializer.instance);
- derializers.put(BigInteger.class, BigIntegerDeserializer.instance);
- derializers.put(BigDecimal.class, BigDecimalDeserializer.instance);
- derializers.put(float.class, FloatDeserializer.instance);
- derializers.put(Float.class, FloatDeserializer.instance);
- derializers.put(double.class, NumberDeserializer.instance);
- derializers.put(Double.class, NumberDeserializer.instance);
- derializers.put(boolean.class, BooleanDeserializer.instance);
- derializers.put(Boolean.class, BooleanDeserializer.instance);
- derializers.put(Class.class, ClassDerializer.instance);
- derializers.put(char[].class, CharArrayDeserializer.instance);
- derializers.put(UUID.class, UUIDDeserializer.instance);
- derializers.put(TimeZone.class, TimeZoneDeserializer.instance);
- derializers.put(Locale.class, LocaleDeserializer.instance);
- derializers.put(InetAddress.class, InetAddressDeserializer.instance);
- derializers.put(Inet4Address.class, InetAddressDeserializer.instance);
- derializers.put(Inet6Address.class, InetAddressDeserializer.instance);
- derializers.put(InetSocketAddress.class, InetSocketAddressDeserializer.instance);
- derializers.put(File.class, FileDeserializer.instance);
- derializers.put(URI.class, URIDeserializer.instance);
- derializers.put(URL.class, URLDeserializer.instance);
- derializers.put(Pattern.class, PatternDeserializer.instance);
- derializers.put(Charset.class, CharsetDeserializer.instance);
- derializers.put(Number.class, NumberDeserializer.instance);
- derializers.put(AtomicIntegerArray.class, AtomicIntegerArrayDeserializer.instance);
- derializers.put(AtomicLongArray.class, AtomicLongArrayDeserializer.instance);
- derializers.put(StackTraceElement.class, StackTraceElementDeserializer.instance);
- derializers.put(Serializable.class, defaultSerializer);
- derializers.put(Cloneable.class, defaultSerializer);
- derializers.put(Comparable.class, defaultSerializer);
- derializers.put(Closeable.class, defaultSerializer);
- try {
- derializers.put(Class.forName("java.awt.Point"), PointDeserializer.instance);
- derializers.put(Class.forName("java.awt.Font"), FontDeserializer.instance);
- derializers.put(Class.forName("java.awt.Rectangle"), RectangleDeserializer.instance);
- derializers.put(Class.forName("java.awt.Color"), ColorDeserializer.instance);
- } catch (Throwable e) {
- // skip
- }
- }
自己实现的类的Deserializer是JavaBeanDeserializer
在getDeserializer的方法中,首先从derializers中取寻找是否有相应的反序列化工具类,对于常用类,在这个时候就能找到相应的反序列化工具类。如果是自己实现的类,最终获得的反序列化工具为JavaBeanDeserializer
- public ObjectDeserializer getDeserializer(Type type) {
- //去map中需找,如果找到为重用类
- ObjectDeserializer derializer = this.derializers.get(type);
- if (derializer != null) {
- return derializer;
- }
- if (type instanceof Class<?>) {
- //不是常用类
- return getDeserializer((Class<?>) type, type);
- }
- 省略
- }
- public ObjectDeserializer getDeserializer(Class<?> clazz, Type type) {
- 省略
- derializer = createJavaBeanDeserializer(clazz, type);
- }
createJavaBeanDeserializer根据实际情况通过asm或者构造器实例化JavaBeanDeserializer
其中有个asmEnable成员变量表示是否通过asm进行实例化。默认android是不通过asm进行实例化的。
- private boolean asmEnable = !ASMUtils.isAndroid();
- public ObjectDeserializer createJavaBeanDeserializer(Class<?> clazz, Type type) {
- 省略
- if (!asmEnable) {
- return new JavaBeanDeserializer(this, clazz, type);
- }
- return ASMDeserializerFactory.getInstance().createJavaBeanDeserializer(this, clazz, type);
- }
JavaBeanDeserializer的构造器通过DeserializeBeanInfo.computeSetters获得解析类的构造器
JavaBeanDeserializer有个私有的,没有setter的成员变量beanInfo,解析类的构造器保存在该成员变量中
- public JavaBeanDeserializer(ParserConfig config, Class<?> clazz, Type type){
- this.clazz = clazz;
- this.type = type;
- //beanInfo中包含了构造器,这就是为什么能解析出对象
- beanInfo = DeserializeBeanInfo.computeSetters(clazz, type);
- for (FieldInfo fieldInfo : beanInfo.getFieldList()) {
- addFieldDeserializer(config, clazz, fieldInfo);
- }
- }
- public static DeserializeBeanInfo computeSetters(Class<?> clazz, Type type) {
- DeserializeBeanInfo beanInfo = new DeserializeBeanInfo(clazz);
- //获取构造器
- Constructor<?> defaultConstructor = getDefaultConstructor(clazz);
- 省略
- }
- public static Constructor<?> getDefaultConstructor(Class<?> clazz) {
- //如果是抽象类或接口,这里返回null,这就是fastjson不支持多态的原因
- if (Modifier.isAbstract(clazz.getModifiers())) {
- return null;
- }
- //不是抽象类或接口,通过反射获得构造器
- Constructor<?> defaultConstructor = null;
- for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
- if (constructor.getParameterTypes().length == 0) {
- defaultConstructor = constructor;
- break;
- }
- }
- 省略
- }
JavaBeanDeserializer的解析json字符串的过程,调用public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName)方法
JavaBeanDeserializer实际的解析json字符串是通过调用deserialze方法进行的。其中就是通过算法解析json字符串而已。这里就不上源码了,值得说的是parser中包含了原始的json字符串。type是解析的类,可能是抽象类或接口。
思路与实现
源码看明白后,思路就很清晰了。
1、实现一个Deserializer,继承自JavaBeanDeserializer,在deserialze方法中解析出json字符串中表明的实现类,并把type变量从抽象类或接口替换成实现类。再将实现类的type传入父类的deserialze方法中。需要注意的是,在构造器中DeserializeBeanInfo.computeSetters已经把beanInfo作为抽象类或接口了,因此需要替换type后再计算一次,并替换原来的beanInfo成员变量。
2、如何比较json字符串中的实现类信息和接口或抽象类中指定的实现类信息,可以有很多办法,我这里通过注释(Annotation)的方式。
3、为了获得我们自定义的Deserializer,需要实现一个ParserConfig类,该类继承自ParserConfig类,重写
getDeserializer方法。
4、哪些类需要通过我们的Deserializer呢,我的做法是创建了一个空的接口,一旦目标类实现了这个接口,在getDeserialize方法中就获得我们自己实现的Deserializer。
接下来,看一下实现代码。两个注释类JsonMaps和JsonMap用来标注json字符串中的实现类信息对应的class name
- /**
- *
- * fastjson扩展,解析多态时申明实现的类
- *
- * @author 吴林峰
- *
- */
- @Target({ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- public @interface JsonMaps {
- JsonMap[] values();
- }
- /**
- *
- * fastjson扩展,申明具体的实现类
- *
- * @author 吴林峰
- *
- */
- @Target({ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- public @interface JsonMap {
- String key();
- Class<?> value();
- }
表明需要多态解析的接口
- /**
- *
- * fastjson,支持多态的标志,为一个空的接口,需要多态解析json的类需要实现该接口
- *
- * @author 吴林峰
- *
- */
- public interface FastJsonPolymorphism {
- }
自定义ParserConfig
- /**
- *
- * 扩展fastjson,支持多态的解析
- *
- * @author 吴林峰
- *
- */
- public class PolymorphismParserConfig extends ParserConfig{
- public PolymorphismParserConfig(){
- super();
- }
- @Override
- public ObjectDeserializer getDeserializer(Class<?> clazz, Type type) {
- //实现了空的接口,表明需要多态解析
- if(clazz==FastJsonPolymorphism.class){
- //返回我们自定义的多态反序列化工具,直接通过构造器,没有用asm
- return new PolymorphismDeserializer(this, clazz, type);
- }
- return super.getDeserializer(clazz, type);
- }
- }
自定义的多态反序列化工具类
- /**
- *
- * 扩展fastjson,对多态支持
- * 目前缺陷,调用了两次bean信息计算DeserializeBeanInfo.computeSetters
- *
- * @author 吴林峰
- *
- */
- public class PolymorphismDeserializer extends JavaBeanDeserializer{
- private static final Logger logger = LoggerFactory.getLogger(PolymorphismDeserializer.class);
- private static final String TARGET_FIELD="objClass";
- private static final String TYPE_NAME_PREFIX = "class ";
- private static final String JAVA_BEAN_DESERIALIZER_BEANINFO_FIELD="beanInfo";
- public PolymorphismDeserializer(DeserializeBeanInfo beanInfo){
- super(beanInfo);
- }
- public PolymorphismDeserializer(ParserConfig config, Class<?> clazz) {
- super(config, clazz);
- }
- public PolymorphismDeserializer(ParserConfig config, Class<?> clazz, Type type) {
- super(config,clazz,type);
- }
- @Override
- public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName){
- String jsonstr=parser.getInput();
- Optional<String> targetKey=Optional.ofNullable(parseTarget(jsonstr));
- if(targetKey.isPresent()){
- logger.debug("目标类的key:"+targetKey.get());
- String className = type.toString();
- if (className.startsWith(TYPE_NAME_PREFIX)) {
- className = className.substring(TYPE_NAME_PREFIX.length());
- }
- try {
- //解析实际类
- Class<?> clazz=Class.forName(className);
- if(clazz.isAnnotationPresent(JsonMaps.class)){
- JsonMaps jsonMaps=clazz.getAnnotation(JsonMaps.class);
- JsonMap[] js=jsonMaps.values();
- boolean flag=false;
- for(JsonMap j : js){
- if(targetKey.get().equals(j.key())){
- Class<?> target=j.value();
- logger.debug("目标类:"+target.getName());
- logger.debug("modifier:"+Modifier.toString(target.getModifiers()));
- type=(Type) target;
- //由于config中getDeserializer方法中,调用JavaBeanDeserializer时已经把抽象类或接口的信息计算了,这里需要替换成实现类的信息
- DeserializeBeanInfo beanInfo=DeserializeBeanInfo.computeSetters(target, type);
- //由于JavaBeanDeserializer中的beanInfo字段为private并且没有setter,只能通过反射设置
- Field beanInfoField=JavaBeanDeserializer.class.getDeclaredField(JAVA_BEAN_DESERIALIZER_BEANINFO_FIELD);
- beanInfoField.setAccessible(true);
- beanInfoField.set(this, beanInfo);
- flag=true;
- break;
- }
- }
- if(!flag){
- logger.error("没有找到指定的类");
- throw new NotFoundJsonMapClassException(targetKey.get());
- }
- }else{
- logger.error("没有指定JsonMaps");
- throw new NotFoundJsonMapsException(type.getTypeName());
- }
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } catch (NoSuchFieldException e) {
- e.printStackTrace();
- } catch (SecurityException e) {
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }else{
- logger.error("没有指定key");
- throw new NotFoundKeyException(jsonstr);
- }
- return super.deserialze(parser, type, fieldName);
- }
- private String parseTarget(String jsonstr){
- String[] ts=jsonstr.split("\""+TARGET_FIELD+"\":");
- return Optional.ofNullable(ts[1])
- .orElse("")
- .split("\"|\"")[1];
- }
- }
demo
假定json字符串如下:{"infoCnt":4,"objClass":"posList","period":15}
objClass为指定实现类的别名。
抽象类
- @JsonMaps(values={
- @JsonMap(key="msg",value=SocketTXSQ.class),
- @JsonMap(key="posList",value=SocketARPL.class),
- @JsonMap(key="pars",value=SocketPARS.class),
- @JsonMap(key="fki",value=SocketFKI.class),
- @JsonMap(key="ici",value=SocketICI.class)
- })
- public abstract class SocketEntity implements FastJsonPolymorphism{
- private int infoCnt;
- private int period;
- public abstract void save(ApplicationContext ctx);
- public int getPeriod() {
- return period;
- }
- public void setPeriod(int period) {
- this.period = period;
- }
- public int getInfoCnt() {
- return infoCnt;
- }
- public void setInfoCnt(int infoCnt) {
- this.infoCnt = infoCnt;
- }
- }
实现类有5个
- public class SocketARPL extends SocketEntity {
- public void save(ApplicationContext ctx){
- System.out.println("实现类posList的save方法");
- }
- }
- public class SocketTXSQ extends SocketEntity {
- public void save(ApplicationContext ctx){
- System.out.println("实现类msg的save方法");
- }
- }
- public class SocketPARS extends SocketEntity {
- public void save(ApplicationContext ctx){
- System.out.println("实现类pars的save方法");
- }
- }
- public class SocketFKI extends SocketEntity {
- public void save(ApplicationContext ctx){
- System.out.println("实现类fki的save方法");
- }
- }
- public class SocketICI extends SocketEntity {
- public void save(ApplicationContext ctx){
- System.out.println("实现类ici的save方法");
- }
- }
我们来看看调用fastjson解析json字符串获得的SocketEntity的结果如何:
- //fastjson使用自带的ParserConfig解析抽象类SocketEntity,结果entity为null
- SocketEntity entity = (SocketEntity) JSON.parseObject(str, SocketEntity.class);
- //fastjson通过我们实现的PolymorphismParserConfig解析抽象类SocketEntity,结果entity为SocketARPL类的实例
- entity = (SocketEntity) JSON.parseObject(str, SocketEntity.class, new PolymorphismParserConfig(), JSON.DEFAULT_PARSER_FEATURE, new Feature[0]);
如果json字符串改成{"infoCnt":4,"objClass":"msg","period":15},那么我们获得的对象为SocketTXSQ。实验表明,我们对fastjson的扩展成功,通过传入PolymorphismParserConfig就可以为抽象类或接口提供解析功能,返回我们期望的实现类的实例。