1. 程式人生 > >一道面試題之關於自定義Json解析器

一道面試題之關於自定義Json解析器

最近在群裡裡面有哥們說在面試的時候,要求上機寫一個簡單的Json解析器,看到這個題目的時候,心裡慌得一比,因為感覺有些力不從心,不知道從哪裡下手,所以趕緊查了一下Gson原始碼,看看有什麼啟示沒有。當然,這篇扯淡並不是介紹Gson原始碼,而是想自定義一個簡單的Json解析器,來熟悉一下Java的反射知識和字串操作知識。

先裝個B,其實也沒有我們想的辣麼難啊,我們先來看看一般簡單的Json字串的格式:

"{'name':'tom', 'age':20 }"

我們該怎麼下手呢?扯淡的話,就不多說了,我現在的想法就是逐個解析,看下圖:


我對字串逐個解析,去掉一些我們認為無用資訊的字元,像啥{

,,'等等字元,直接解析出我們想要的name作為我們的key值,在提前知道要解析的JavaBean物件時,通過反射的方法知道name將要對應的屬性型別,比如說它是String型別,那麼我再次對字串逐個解析,將解析的String型別字串通過反射方法賦值給JavaBean物件的屬性物件,那麼我們Json解析器就完成了。雖然說了這麼多,也不知道說清楚沒有,還是通過程式碼說明一下吧:

說先我們可以定義一個User類,原始碼如下;

public class User {
    private String name;
    private int age ;

    //getter/setter方法省
}

通過反射方法,得到User類的屬性值和屬性型別:

Field[] fields = User.class.getDeclaredFields();
    for(Field f : fields) {
        System.out.println(f.getName() + "--->" + f.getType());
    }

得到的結果為:

name--->class java.lang.String
age--->int

好了,我們現在知道了name屬性是String型別的,age屬性是int型別的,現在我們就準備解析Json字串類了。

我們定義一個Reader物件,來獲取相應的Json屬性。【因為這是一道面試題,更多的是考察對Json認知的過程,所有下面的程式碼只是簡單的介紹思路,問題肯定是有】:

public class Reader {
    //去掉空格和一些非必須字元
    public void nextWithNoSpace() {
        //...
    }

    //獲取下一段符合要求的String字串
    public String getNextString() {
        //...
    }

    //獲取下一段符合要求的int
    public String getNextInt() {
        //...
    }

    //資料是否解析完成
    public boolean dataHasEnd() {
        //...
    }

    //當然你還可以新增獲取Double,char,byte,short,boolean等型別的資料
}

好了,思路到這,我們可以解析資料了,具體虛擬碼如下:

String jsonStr = "{'name':'tom', 'age':20 }" ;

//將jsonStr目標字串進行解析
Reader reader = new Reader(jsonStr);

//迴圈解析字串:
while(!reader.dataHasEnd()) {

    //獲取目標端的String字串
    String nextStr = reader.getNextString(); 

    //通過反射獲取User的例項方法:
    //這也就是為啥Json解析時,所有的JavaBean都必須含有一個無引數構造器的原因
    User user = User.class.getConstructor().newInstance()

    //通過反射獲取User的屬性
    Field[] fields = User.class.getDeclaredFields();
    //遍歷所有的屬性,查詢屬性名與nextString對應的欄位
    //這也就是為啥JavaBean對應的欄位與Json中對應的屬性名一致了

    for (Field field : fields) {
        if(field.getName().equals(name)) {
            //如果一致,檢視這個屬性是什麼型別的:

            //如果是Int型別的,那麼可以告訴Reader,下一段解析你給我出個int型別的資料就行
            if(field.getType()  == Integer.class || type == int.class) { 
                //獲取目標資料
                int value = reader.getNextInt();

                //通過反射將目標值注入:
                if(!field.canAccess(object)) {
                    field.setAccessible(true);
                    field.set(user, params);
                }
            } 

            //String型別資料
            else if(field.getType() == String.class) {
                String value = reader.getNextString();
                //同理直接注入
            } 

            else {

                //....其他型別資料

            }
        }
    }
}

最終呼叫方式為:

private static final String JSON = "{'age':'25', 'name':'Steven' , 'data' : {'address':'sh'}}";
public static void main(String[] args) {
    Object object = new MyGson().fromJson(JSON, User.class);
    System.out.println(object);
}

呼叫結果為:

解析完成了,由於面試的時候比較緊張,但還是寫出來了。其實你回家看看Json的原理,大致的原理和這個是差不多的,只不過人家考慮的東西非常多,像什麼序列化/反序列化,泛型,各種註解,各種class巢狀和組合,不過大致的想法是和我們一致的,這裡主要說一下思路,一開始拿到題目的時候還是懵逼的,不過慢慢分析就就不難了,還是平時需要多看原始碼啊,不然真的是知其然不知其所以然了啊。