1. 程式人生 > >Simplify-Core -- java物件轉換成Json(Json generator)

Simplify-Core -- java物件轉換成Json(Json generator)

先感嘆一波今天正式畢業,拿到了畢業證和學位證,然後部落格也有三個多月沒寫了,最近自己寫了一個工具類的專案,Simplify,旨在簡化重複的JAVA程式碼,基於JDK8,無其它jar包依賴,提供序列化,json,日期等常見操作。json generator部分寫的比較完善了, parse部分能跑簡單的測試用例。
網上json的工具類數不勝數,自己寫主要還是實現最基本的json功能滿足大部分的需要,更重要是鍛鍊編碼水平。Simplify裡的json測試用例都以fastjson為標準,實現原理也借鑑了fastjson,但是水平有限,能看懂別人的程式碼才能借鑑,大部分沒看懂只能自己琢磨了。這裡先寫generator部分的實現原理。分享給大家。

專案地址:https://github.com/lovejj1994/Simplify-Core

歡迎路過的大牛對專案提出建議。

整個Json框架需要做哪些準備?

1.需要一個公開的入口,讓使用者簡單的呼叫靜態方法就可以實現java物件轉換為json格式的字串。
2.再往深一層,對於一個java物件,裡面有許多成員變數,變數有不同的型別,對於不同的型別物件,需要有不同的處理方法,所以不同的型別要有專門的“處理器”(codec),為了規範這些“處理器”,需要設計一個介面(IJson),定義專門的方法(Object writeJsonString(Object o))返回處理後的字串。
3.整個流程中,反射充當訪問物件的重要途徑,對於一個物件中的不同成員變數,要定義一個規則:只能通過公有(public)的get,set方法才能訪問到這個變數,雖然反射可以暴力訪問,但是對於使用者來說,寫私有的,或者不符合編碼規範(不是駝峰式)的get,set方法的成員變數,使用者並不想你去訪問它並且轉換為json格式。

這裡寫圖片描述

具體設計

1.建立一個Json類,裡面有一個toJsonString()方法,這是唯一給使用者使用的入口。

Json.java

private static JsonSerializer jsonSerializer;

public static String toJsonString(Object t) {
   if (Objects.isNull(t)) {
       return null;
   }
   jsonSerializer = JsonSerializer.getInstance();

   return jsonSerializer.convertToJsonString(t);
}
  1. JsonSerializer 是專門負責 排程Json generator 的各種方法,java物件轉json格式字串 都由它負責。
    AbstractJson 是一個抽象類,負責存放一些公共方法,在convertToJsonString方法中,有一個getSuitableHandler()方法,它就定義在AbstractJson 中,它的責任在於根據傳進去的object找到合適的codec。
public class JsonSerializer extends AbstractJson {

    private static Logger logger = Logger.getLogger(JsonSerializer.class.getName());

    private static JsonSerializer jsonSerializer = new JsonSerializer();

    private JsonSerializer() {
    }

    public static JsonSerializer getInstance() {
        return jsonSerializer;
    }

    //getSuitableHandler
    public String convertToJsonString(Object o) {
        Class<?> c = o.getClass();
        IJson suitableHandler = getSuitableHandler(c);
        return (String) suitableHandler.writeJsonString(o);
    }

}

getSuitableHandler方法檢測傳進去的class型別,然後返回合適的codec。

protected IJson getSuitableHandler(Class c) {

    if (Collection.class.isAssignableFrom(c)) {
         c = Collection.class;
     }

     if (Map.class.isAssignableFrom(c)) {
         c = Map.class;
     }

     if (Number.class.isAssignableFrom(c)) {
         c = Number.class;
     }

     if (c.isArray()) {
         return new ArrayCodec();
     }

     switch (c.getTypeName()) {
         case Const.NUMBER_TYPE:
             return new NumberCodec();
         case Const.COLLECTION_TYPE:
             return new CollectionCodec();
         case Const.MAP_TYPE:
             return new MapCodec();
         case Const.STRING_TYPE:
             return new StringCodec();
         case Const.BOOLEAN_TYPE:
             return new BooleanCodec();
         case Const.CHAR_TYPE:
             return new CharCodec();
         case Const.DATE_TYPE:
             return new DateCodec();
         case Const.LOCALDATE_TYPE:
             return new LocalDateCodec();
         case Const.LOCALDATETIME_TYPE:
             return new LocalDateTimeCodec();
         case Const.LOCALTIME_TYPE:
             return new LocalTimeCodec();
         default:
             return new ObjectCodec();
     }
 }

部分codec展示:

/**
 * Char 解析器
 * Created by panqian on 2017/6/6.
 */
public class LongCodec extends AbstractJson implements IJson {

    StringBuffer sb;

    @Override
    public Object writeJsonString(Object o) {
        Long l = (Long) o;
        sb = new StringBuffer(1);
        numberHandle(sb, l);
        return sb.toString();
    }

    @Override
    public Object parse(Object o, Method m) {
        if (Objects.isNull(o)) {
            return null;
        }
        BigInteger bi = new BigInteger(o + "");
        return bi.longValue();
    }
}

/**
 * Number 解析器
 * Created by panqian on 2017/6/6.
 */
public class NumberCodec extends AbstractJson implements IJson {

    StringBuffer sb;

    @Override
    public Object writeJsonString(Object o) {
        Number n = (Number) o;
        sb = new StringBuffer(n.toString().length());
        numberHandle(sb, n);
        return sb.toString();
    }

    @Override
    public Object parse(Object o, Method m) {
        BigDecimal bd = (BigDecimal) o;
        return bd.doubleValue();
    }
}

codec實現IJson介面, 規定好了codec的規範,writeJsonString用於 轉換Json,parse用於解析json。

**
 * Created by panqian on 2017/6/6.
 */
public interface IJson extends Serializable {
    Object writeJsonString(Object o);

    Object parse(Object o, Method m);
}

3.簡單的基本型別可以直接找到合適的 codec,對於自定義型別,需要用ObjectCodec進行成員變數分解,每個成員變數重新找自己合適的codec,然後執行writeJsonString返回合格的json格式,對於object的分解是一種遞迴呼叫。

object分解遞迴程式碼,特別注意serializerObject方法。

/**
 * Object 解析器
 * Created by panqian on 2017/6/8.
 */
public class ObjectCodec extends AbstractJson implements IJson {

 private static Logger logger = Logger.getLogger(JsonSerializer.class.getName());

 StringJoiner sj;

 private String serializerObject(Object o) {
     sj = new StringJoiner(Const.COMMA, Const.PRE_BRACE, Const.POST_BRACE);
     Class<?> cClass = o.getClass();

     //查詢該類所有宣告的方法(除Object)
     List<Method> allDeclaredMethods = ReflectionUtils.getAllDeclaredMethods(cClass);

     //篩選public get方法
     ArrayList<Method> publicGetMethods = new ArrayList<>();
     if (null != allDeclaredMethods && allDeclaredMethods.size() > 0) {
         for (Method m : allDeclaredMethods) {
             String modifier = ReflectionUtils.getModifier(m);
             if (modifier.contains(Const.PUBLIC) && m.getName().contains(Const.GET)) {
                 publicGetMethods.add(m);
             }
         }
     }

     if (null != publicGetMethods && publicGetMethods.size() > 0) {

         Collections.sort(publicGetMethods, (x, y) -> Collator.getInstance().compare(x.getName(), y.getName()));
         for (Method method : publicGetMethods) {
             String name = method.getName();
             String substring = name.substring(3, name.length());
             char c = substring.charAt(0);
             if (c >= 'A' && c <= 'Z') {
                 Character b = (char) (c + 32);
                 String key = b.toString().concat(substring.substring(1, substring.length()));
                 try {
                     Object invoke = method.invoke(o);
                     if (Objects.nonNull(invoke)) {
                         sj.add(Const.SINGLE_QUOTES + key + Const.SINGLE_QUOTES + Const.COLON + JsonSerializer.getInstance().convertToJsonString(invoke));
                     }
                 } catch (IllegalAccessException e) {
                     logger.severe(e.getMessage());
                 } catch (InvocationTargetException e) {
                     logger.severe(e.getMessage());
                 }
             }
         }
     }

     return sj.toString();
 }


 @Override
 public Object writeJsonString(Object o) {
     String result = serializerObject(o);
     return result;
 }

 @Override
 public Object parse(Object o, Method m) {
     JsonObject jo = (JsonObject) o;
     Type[] genericParameterTypes = m.getGenericParameterTypes();
     Type t = null;
     for (Type type : genericParameterTypes) {
         if (ParameterizedType.class.isAssignableFrom(type.getClass())) {
             for (Type t1 : ((ParameterizedType) type).getActualTypeArguments()) {
                 t = t1;
             }
         }
     }

     try {
         Class<?> aClass = Class.forName(t.getTypeName());
         Object o1 = aClass.newInstance();
         //查詢該類所有宣告的方法(除ObjectList<Method> allDeclaredMethods = ReflectionUtils.getAllDeclaredMethods(aClass);

         //篩選public set方法
         ArrayList<Method> publicSetMethods = new ArrayList<>();
         if (null != allDeclaredMethods && allDeclaredMethods.size() > 0) {
             for (Method md : allDeclaredMethods) {
                 String modifier = ReflectionUtils.getModifier(md);
                 if (modifier.contains(Const.PUBLIC) && md.getName().startsWith(Const.SET)) {
                     publicSetMethods.add(md);
                 }
             }
         }

         if (null != publicSetMethods && publicSetMethods.size() > 0) {
             for (Method md : publicSetMethods) {
                 String methodName = md.getName();
                 String variable = methodName.substring(3, methodName.length());
                 Class<?>[] parameterTypes = md.getParameterTypes();
                 Class parameterType = null;
                 if (null != parameterTypes && parameterTypes.length == 1) {
                     parameterType = parameterTypes[0];
                 }
                 variable = variable.substring(0, 1).toLowerCase() + variable.substring(1, variable.length());
                 if (jo.containsKey(variable)) {
                     Object oo = jo.get(variable);
                     IJson suitableHandler = getSuitableParseHandler(parameterType);
                     Object parse = suitableHandler.parse(oo, md);
                     try {
                         md.invoke(o1, parse);
                     } catch (IllegalAccessException e) {
                         e.printStackTrace();
                     } catch (InvocationTargetException e) {
                         e.printStackTrace();
                     }
                 }
             }
         }
         return o1;
     } catch (ClassNotFoundException e) {

     } catch (IllegalAccessException e) {
         e.printStackTrace();
     } catch (InstantiationException e) {
         e.printStackTrace();
     }
     return null;
 }
}

上面是大致的Json generate 流程,使用規則 專案裡有完整的測試用例。