Десериализация fastJson обрабатывает дженерики, что я могу узнать из этого

Java

Разбор JSON в городских условиях

В нашей повседневной работе мы часто формулируем или сталкивайтесь с такой структурой JSON

{
  "resultcode": "200",
  "reason": "成功的返回",
  "result": {
    "area": "浙江省温州市平阳县",
    "sex": "男",
    "birthday": "1989年03月08日"
  }
}

Для провайдера этого интерфейса самый внешнийresultcode reasonиresultВсе три элемента присутствуют. Таким образом, мы можем определить такойResponseсвоего рода

public class Response<T> {
    private String resultcode;

    private String reason;

    private T result;

}

Используйте дженерики, чтобы решить, чем в конечном итоге будет заполнен результат. Конечно, то, что здесь заполнено, является объектом, и мы также можем определить его какUserсвоего рода

public class User {
    private String area;
    private String sex;
    private String birthday;
}

Таким образом, наши данные могут быть возвращены вызывающей стороне в соответствии с правилами соглашения примера json. В качестве вызывающей стороны, то есть машины, которая получает эти данные, нам нужно определить следующий классResopnseUser

public class ResopnseUser {
    private String resultcode;

    private String reason;

    private User result;
}

public class User {
    private String area;
    private String sex;
    private String birthday;
}

Затем мы можем использовать fastJson для удачного синтаксического анализа.

        String  testJson = "{\"resultcode\":\"200\",\"reason\":\"成功的返回\",\"result\":{\"area\":\"浙江省温州市平阳县\",\"sex\":\"男\",\"birthday\":\"1989年03月08日\"}}";
        ResponseUser response1 =  JSONObject.parseObject(testJson,ResponseUser.class);

Как видите, мы получаем желаемые результаты.

Подумайте об улучшениях

Однако это имеет менее серьезные последствия. Каждый раз, когда я подключаю интерфейс к провайдеру, мне приходится генерировать соответствующий класс. Со временем эти специализированные классы будут становиться все более и более глубоко вложенными. Поскольку провайдер может абстрагироватьResponseclass, так я могу сделать это и с провайдером?

        String  testJson = "{\"resultcode\":\"200\",\"reason\":\"成功的返回\",\"result\":{\"area\":\"浙江省温州市平阳县\",\"sex\":\"男\",\"birthday\":\"1989年03月08日\"}}";
        Response<User> response =  JSONObject.parseObject(testJson,Response.class);

К сожалению, результаты, которые мы получили, оказались не такими, как мы ожидали, удался только внешний парсинг данных, а внутреннийresultвсе ещеJSONObject.

форум для помощи

К счастью, это решение не единственное среди тысяч инженеров, уже решивших подобного рода задачи! С помощью различных поисков мы обнаружили, что fastJson предоставляетTypeReferenceЭтот класс может решить нашу проблему.

String  testJson = "{\"resultcode\":\"200\",\"reason\":\"成功的返回\",\"result\":{\"area\":\"浙江省温州市平阳县\",\"sex\":\"男\",\"birthday\":\"1989年03月08日\"}}";
        Response<User> response =  JSONObject.parseObject(testJson,new TypeReference<Response<User>>(){});

Очень хорошо, мы получили желаемый результат, и, наконец, мы можем унифицировать оценку самого внешнего возвращаемого значения всех интерфейсов.

никогда не останавливайся здесь

И так далее, вопросов ко мне не нашлось? Обобщения во время выполнения компиляциистирание типаКакие! Это также первый раз, когда мы напрямую используемResponse<User> response = JSONObject.parseObject(testJson,Response.class);Неверная причина. Ну, даже если мы напишемnew TypeReference<Response<User>>(){}Его тоже не стереть? Как он переходит к фактическому типу, который мы определили во время выполнения? просмотревTypeReferenceК счастью, исходный код очень прост, а объем кода невелик. Первое, что нужно ввести, это его конструкторprotected TypeReference(), путем отладки мы обнаружили, что написанный нами универсальный тип был получен при выполнении кода до второй строки.

        Type superClass = getClass().getGenericSuperclass();

        Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];

Этот код очень прост, перейдите к его родительскому классуgetGenericSuperclass()Фактический тип получен, продолжая следить за кодом, мы можем обнаружить, что он вызывает нативный методprivate native String getGenericSignature0();Получить некоторую информацию о классе и родительском классеClassRepositoryиType. В этот момент давайте оглянемся назадnew TypeReference<Response<User>>(){}На самом деле создалTypeReferenceАнонимный внутренний класс класса черезgetGenericSuperclass()Получите информацию о фактической информации.

продолжать копать

Давайте теперь рассмотрим, что у нас получилось, и отметим 2 момента:

  1. Дженерики выполняются во время компиляциистирание типа.
  2. получить его родительский классgetGenericSuperclass()Чтобы добраться до фактического типа. И т.д., эти два взаимно противоречат ах. 1 сказал нет, два сказали найду, в итоге как получилось? Просмотрев файлы, скомпилированные с помощью байт-кода, мы нашли ответ.
    Вот код, который содержитResponse<User> response = JSONObject.parseObject(testJson,new TypeReference<Response<User>>(){});Декомпилированная информация файла класса метода. Мы видим, что у него есть еще одинLocalvariableTypeTable,внутриSignatureСохраняется только фактическая информация о типе. **То есть тип не стирается полностью, и мы все еще можем получить тип параметра через отражение. ** Так называемое стирание, просто поставьте методcodeБайт-код атрибута стирается. Кроме того, мы также виделиInnerClassesВ списке есть внутренний класс Main$1, который принадлежит нам.new TypeReference<Response<User>>(){}

Суммировать

  1. в состоянии пройтиJSONObject.parseObject(testJson,new TypeReference<Response<User>>(){});способ решить проблему вложенных дженериков в json. Удобно, быстро и интуитивно понятно
  2. в состоянии пройтиgetClass().getGenericSuperclass();Получите настоящий тип. код верификации
  // 这类创建了一个HashMap的匿名子类
        HashMap<String,Integer> subIntMap = new HashMap<String,Integer>(){};

        System.out.println(subIntMap.getClass().getSuperclass());

        Type subClassType = subIntMap.getClass().getGenericSuperclass();
        if(subClassType instanceof ParameterizedType){
            ParameterizedType p = (ParameterizedType) subClassType;
            for (Type t : p.getActualTypeArguments()){
                System.out.println(t);
            }
        }

выходной результат

class java.util.HashMap
class java.lang.String
class java.lang.Integer
  1. Тип полностью не стирается, а тип параметра можно получить путем отражения. код верификации
 ResponseUser response = new ResponseUser();
        Field field = response.getClass().getField("result");
        System.out.println("result Type is "+ field.getType());
        System.out.println("result GenericType is "+ field.getGenericType());
public class ResponseUser {
    private String resultcode;

    private String reason;

    public List<User> result;
}

выходной результат

result Type is interface java.util.List
result GenericType is java.util.List<com.jd.jr.alpha.cpa.fastJson.User>