1. 程式人生 > >如何利用反射,解析json

如何利用反射,解析json

java中反射的地位可想而知,有的人說反射就是程式設計師的春天,現在關於java的框架,反射幾乎都會用到,因為確實很方便,說的那麼多,那就開始一起探討吧。
如果用過jdbc的話,我們是怎麼載入驅動的呢?我們都會寫這樣一串程式碼

Class.forName("com.mysql.jdbc.Driver");

其實這裡就是用到反射了。這是開場白,那我們就慢慢深入吧,首先新建一個類用於操作

public class Student{
    public String name;
    public Integer age;
    public String sex;
}

很簡單的一個類,我們仿照jdbc載入驅動的方式,我們自己也操作一遍.我是在android環境下測試的。

  try {
            Class<?> aClass = Class.forName("com.myapplication.Student");
            Log.i("data-->", aClass.getSimpleName());
        } catch (Exception e) {
            e.printStackTrace();
        }

打印出來的就是Student,Class.forName裡的就是這個類在這個專案裡的真實路徑,很顯然加載出了這個類,那我們就通過這種方式例項化這個類。

    try {
            Class<?> aClass = Class.forName("com.myapplication.Student");
            Object o = aClass.newInstance();
            Log.i("data-->", String.valueOf(o.getClass()));
        } catch (Exception e) {
            e.printStackTrace();
        }

打印出來的就是Student這個類的名稱,這就例項化了這個類了,那我們如何通過反射往這個類裡設定值呢?
封裝了一個方法主要為了方便

 private void showLog(Object name) {
        Log.i("data-->", String.valueOf(name));
    }

首先就是如何知道類裡的所有的屬性,其實也很簡單

  try {
            Class<?> aClass = Class.forName("com.myapplication.Student");
            Object o = aClass.newInstance();
            Field[] fields = aClass.getFields();
            for (int i = 0; i < fields.length; i++) {
                showLog(fields[i].getName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

但是這種方法只能針對屬性是public型別的,如果是私有的話這種方法就不行了,因為這種方法只能獲取public型別的,其實我們首先要了解反射中的兩個方法的區別
getFields()只能獲取public的欄位,包括父類的,而getDeclaredFields()只能獲取自己宣告的各種欄位,包括public,protected,private。

        try {
            Class<?> aClass = Class.forName("com.myapplication.Student");
            Object o = aClass.newInstance();
            Field[] fields = aClass.getDeclaredFields();
            for (Field field : fields) {
            //賦予獲取
               field.setAccessible(true);
               showLog(field.getName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

這樣的話就可以了。那就開始設定值了.

 try {
            Class<?> aClass = Class.forName("com.myapplication.Student");
            Object o = aClass.newInstance();
            Field name = aClass.getDeclaredField("name");
            name.setAccessible(true);
            name.set(o,"小明");
            Student student = (Student) o;
            showLog(student.name);
        } catch (Exception e) {
            e.printStackTrace();
        }

打印出了小明.但是我們如果在不知道類的屬性名稱的時候又該怎麼設定值呢?

 try {
            Class<?> aClass = Class.forName("com.myapplication.Student");
            Object o = aClass.newInstance();
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                if(field.getGenericType().toString().equals("class java.lang.Integer")){
                    showLog("");
                    field.set(o,12);
                }else  if(field.getGenericType().toString().equals("class java.lang.String")){
                    field.set(o,"小明");
                }
            }
            Student student = (Student) o;
            showLog(student.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }

getGenericType()方法是獲取屬性型別的,不同型別的屬性要轉換,不然會報型別轉換異常的.
但是我們在使用反射的時候一定要注意一個問題就是一個類一定要有無參構造方法,不然就報錯,為什麼這樣說呢,就是因為我們用反射例項化物件,就是呼叫無參構造方法,
現在我們只是採用屬性名,那通過set和get方法又如何操作呢?

 try {
            Class<?> aClass = Class.forName("com.myapplication.Student");
            Object o = aClass.newInstance();
            Method setName = aClass.getDeclaredMethod("setName", String.class);
             setName.setAccessible(true);//這個是賦予許可權的,可以獲取私有的
            setName.invoke(o,"小明");
            Student student = (Student) o;
            showLog(student.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }

又如何根據構造方法例項化物件呢?

     try {
            Class<?> aClass = Class.forName("com.myapplication.Student");
           
            Constructor<?> constructor = aClass.getDeclaredConstructor(String.class, Integer.class, String.class);
            //getDeclaredConstructor裡的引數就是構造方法的引數型別
            Object o1 = constructor.newInstance("小明", 12, "男");//通過這種方式傳參
            Student student = (Student) o1;
            showLog(student.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }

其實不管用哪個方式,會發現有個共性,不管哪種方式,都會有兩種方法,一個根據型別獲取,一個獲取全部,只需要我們記住一個,另一個也能猜到的,反射的應用場景很多,我們現在就舉個例子就是怎麼解析json,然後把資料封裝成java類.

 try {
            String json = "{\"name\":\"小明\",\"age\":12,\"sex\":\"男\"}";
            JSONObject jsonObject = new JSONObject(json);
            Class<?> aClass = Class.forName("com.myapplication.Student");
            Object obj = aClass.newInstance();
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                if (field.getGenericType().toString().equals(String.class.toString())) {
                    field.set(obj, jsonObject.getString(field.getName()));
                } else if (field.getGenericType().toString().equals(Integer.class.toString())) {
                    field.set(obj, jsonObject.getInt(field.getName()));
                }
            }
            Student student = (Student) obj;
            showLog(student);
        } catch (Exception e) {
            e.printStackTrace();
        }

很簡單的就實現了,其實這是可以封裝起來的,利用泛型那我們就封裝一下,

 public static <T> T getJsonClass(String json, Class<T> clzz) {
        try {
            JSONObject jsonObject = new JSONObject(json);
            Object obj = clzz.newInstance();
            Field[] declaredFields = clzz.getDeclaredFields();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                if (field.getGenericType().toString().equals(String.class.toString())) {
                //json的值也要判空的,不然資料為空的話,會出現莫名其妙的錯誤,我太倉促沒加,寫程式就要時刻記得判空,才能提高效率
                    field.set(obj, jsonObject.getString(field.getName()));
                } else if (field.getGenericType().toString().equals(Integer.class.toString())) {
                    field.set(obj, jsonObject.getInt(field.getName()));
                }
                //其他的型別可以自行加入,這樣就可以解析json,然後轉換成實體類
            }
            return (T) obj;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

其實呼叫就只是一行程式碼就可以了,是不是簡單很多

Student student = getJsonClass(json, Student.class);

反射和泛型可謂是很好的組合,因為框架都會用到的,還有一個就是註解的知識,之後我會講述的,利用註解、反射和泛型實現一個簡單的實體類注入的專案,相信接觸過Spring的小夥伴都知道吧。