Gson 프레임 워 크 학습

http://www.jianshu.com/p/89c314ae8c0b
소개 하 다.
Gson (일명 구 글 Gson) 은 구 글 이 발표 한 오픈 소스 코드 자바 라 이브 러 리 로 자바 대상 을 JSON 문자열 로 정렬 하거나 JSON 문자열 을 자바 대상 으로 역 직렬 화 하 는 데 사용 된다.
기본 개념
  • Serialization: 직렬 화, 자바 대상 을 JSon 문자열 로 만 드 는 과정.
  • Deserialization: 역 직렬 화, 문자열 을 자바 대상 으로 변환 합 니 다.
  • JSON 데이터 의 JSonElement 는 다음 과 같은 네 가지 유형 이 있 습 니 다.
  • JsonPrimitive ——           
    JsonObject——     JsonElement   (    String)       。        JsonObject      JsonElement       。
    JsonArray—— JsonElement    。                    ,         。
    JsonNull——   null
    

    질문
  • toString () 과 구조 방법 과 같은 간단 한 체 제 를 제공 하여 자바 대상 과 JSon 간 의 상호 전환 을 실현 한다.
  • 이미 존재 하 는 바 꿀 수 없 는 대상 을 JSon 으로 전환 하거나 JSon 이 존재 하 는 대상 으로 전환 할 수 있 습 니 다.
  • 사용자 정의 대상 의 표현 형식 을 허용 합 니 다.
  • 임의의 복잡 한 대상 을 지원 한다.
  • 압축 가능 하고 읽 을 수 있 는 JSon 의 문자열 출력 을 생 성 할 수 있 습 니 다.

  • gson 주해
    소스 코드 분석
    Gson 은 사실 모델 변환 도구 입 니 다. 그의 한쪽 은 json 데이터 형식 인 코딩 흐름 이 고 다른 한쪽 은 우리 의 자바 대상 모델 입 니 다.우 리 는 먼저 자신 에 게 입구 점 을 찾 아 Gson 에서 흔히 볼 수 있 는 예 를 보 자.
    public static class ClassRoom {
          public String roomName;
          public int number;
    
          public String toString() {
             return "[" + roomName + ":" + number + "]";
          }
     }
    
     public static class User {
          public String name;
          public int age;
          private ClassRoom room;
    
          @Override
          public String toString() {
           // TODO Auto-generated method stub
           return name + "->" + age + ":" + room;
          }
     }
    
    

    우 리 는 json 형식의 데이터 문자열 을 전송 하려 고 시도 했다.
    Gson gson = new Gson();
    String strJson = "{name:'david',age:19,room:{roomName:'small',number:1}}";
    User u = gson.fromJson(strJson, User.class);
    

    상기 코드 에서 gson 은 데이터 어댑터 의 역할 을 하고 JSon 의 데이터 형식 을 자바 업무 에 사용 할 수 있 는 데이터 모델 로 변환 하 는 것 과 같다.이 층 의 전환 은 Gson 의 from JSon 방법 을 통 해 이 루어 졌 다.
    public  T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
        if (json == null) {
          return null;
        }
        StringReader reader = new StringReader(json);
        T target = (T) fromJson(reader, typeOfT);
        return target;
      }
    
    

    Gson 은 스 트림 방식 으로 문 자 를 읽 는 것 을 지원 합 니 다. 인터페이스의 재 활용 성 을 위해 첫 번 째 매개 변 수 를 String 대상 으로 입력 하면 Gson 처럼 스 트림 대상 StringReader 로 포장 합 니 다.Gson 의 from JSon 은 통 일 된 인터페이스 방법 T from JSon (Json Reader reader, Type type: OfT) 으로 호출 됩 니 다.
    //code 2
     public  T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
            boolean isEmpty = true;
            boolean oldLenient = reader.isLenient();
            reader.setLenient(true);//     Json    
            try {
                reader.peek();
                isEmpty = false;
                TypeToken typeToken = (TypeToken) TypeToken.get(typeOfT);
                TypeAdapter typeAdapter = getAdapter(typeToken);
                T object = typeAdapter.read(reader);
                return object;
            } catch (EOFException e) {
                   ............
            }
        }
    

    code 2 코드 에서 주로 다음 과 같은 일 을 했 습 니 다.
  • 우리 가 들 어 온 StringReader 는 다시 JSon 문법 형식의 JSonReader 대상 으로 포 장 됩 니 다.
  • setLenient 방법 을 호출 하여 JSon 문법 에 더욱 큰 관용 도 를 제공한다.
  • reader. peek 방법 을 통 해 문법 검 사 를 한 번 하고 첫 번 째 비 어 있 는 문 자 를 가 져 옵 니 다. 이 비 어 있 는 문 자 를 통 해 다음 해석 형식 을 표시 합 니 다.
  • 들 어 오 는 타 입 타 입 을 통 해 해당 타 입 Token 을 가 져 옵 니 다.
  • 생 성 된 TypeToken 을 통 해 해당 하 는 어댑터 를 생 성 합 니 다.(Gson 의 역할 은 Json 데이터 모델 을 서로 다른 플랫폼 의 자바 대상 으로 전환 하 는 것 으로 실제 적 으로 플랫폼 인터페이스의 전환 이 라 고 할 수 있다. 여기 서 Gson 은 어댑터 의 방식 으로 이 방안 을 실현 했다).우 리 는 코드 를 한 걸음 한 걸음 보 았 다. 코드 가 들 어 오 자마자 reader 는 peek 조작 을 호출 했다. 이것 은 왜 일 까?스 트림 대상 에서 peek 의 목적 은 다음 문 자 를 보기 위해 서 라 는 것 을 잘 알 고 있 습 니 다. Json Reader 도 마찬가지 입 니 다.JSonReader 는 peek 를 통 해 간단 한 문법 검 사 를 한 다음 현재 JSonReader 가 해석 할 때 어떤 형식 으로 해석 할 지 표시 합 니 다.
  • //code3
    public JsonToken peek() throws IOException {
        int p = peeked;
        if (p == PEEKED_NONE) {
          p = doPeek();
        }
       ....
    }
    

    peek 방법 은 이전 스 트림 파일 처럼 실제 데 이 터 를 되 돌려 주 는 것 이 아니 라 현재 의 분석 상태 표지 인 JsonToken 을 되 돌려 줍 니 다.JSonToken 은 매 거 진 유형 으로 JSon 데이터 형식 에 표 시 된 각종 문법 단위 입 니 다.peek () 함수 내부 에서 doPeek () 를 호출 했 습 니 다.두 이름 은 똑 같 았 지만 돌아 온 결 과 는 전혀 달 랐 다.doPeek 은 진정한 의미 에서 데 이 터 를 되 돌려 주 는 것 입 니 다. 즉, 진실 한 문 자 를 되 돌려 주 는 것 입 니 다.
    //code doPeek()
     private int doPeek() throws IOException {
          stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;
           switch (c) {
                    case '"':
                        return peeked = PEEKED_DOUBLE_QUOTED_NAME;
                    case '\'':
                        checkLenient();
                        return peeked = PEEKED_SINGLE_QUOTED_NAME;
                    case '}':
                        if (peekStack != JsonScope.NONEMPTY_OBJECT) {
                            return peeked = PEEKED_END_OBJECT;
                        } else {
                            throw syntaxError("Expected name");
                        }
                    default:
          }
      }
    
    

    doPeek () 함수 에 stack 변수 라 는 데 이 터 를 사용 해 야 합 니 다. 그 안에 저 장 된 것 은 JsonScope 라 는 상수 입 니 다.이것 은 Gson 이 JSon 에 대한 분석 방식 은 스 택 식 해석 을 사용 하고 차원 에 따라 분석 하 는 방식 이기 때문에 이런 차원 의 분석 방식 은 스 택 으로 이전의 상 태 를 기록 해 야 하기 때문이다.이 stack 의 초기 값 은 초기 코드 블록 에 설정 되 어 있 습 니 다:
    
     private int[] stack = new int[32];
      public int stackSize = 0;
      {
          stack[stackSize++] = JsonScope.EMPTY_DOCUMENT;
      }
    

    이 를 통 해 알 수 있 듯 이 stack 초기 스 택 지붕 데 이 터 는 EMPTY 입 니 다.DOCUMENT 상수.이때 우 리 는 doPeek 함수 로 돌아 갔다.우 리 는 볼 수 있다.
  • if 는 line 1 의 방법 체 를 호출 합 니 다.창고 꼭대기 데 이 터 를 NONEMPTY 로 대체DOCUMENT 상태
  • line 2 에서 다음 비 공백 문 자 를 가 져 오고 c 변수
  • 에 값 을 부여 합 니 다.
  • line 3 에서 case '{' 를 실행 하여 PEEKED BEGIN OBJECT 정형 상수 를 peeked 변수 에 지불 하고 되 돌려 줍 니 다. 그리고 우 리 는 peek () 방법 을 되 돌려 줍 니 다.
  • int p = peeked;
        if (p == PEEKED_NONE) {
          p = doPeek();
        }
        switch (p) {
        case PEEKED_BEGIN_OBJECT:
          return JsonToken.BEGIN_OBJECT;
    }
    

    doPeek 방법 은 정형 의 상수 로 되 돌아 간 후 peek 방법 은 이 정형 상수 로 대응 하 는 JSonToken 변수 로 바 뀌 었 습 니 다. 어떤 관리 들 은 물 어 볼 수도 있 습 니 다. 그렇다면 JSonToken 의 의 미 는 무엇 입 니까? JSonToken 의 데 이 터 를 되 돌아 오 는 peeked 변수 로 대체 할 수 있 습 니까?
    case PEEKED_SINGLE_QUOTED_NAME:
        case PEEKED_DOUBLE_QUOTED_NAME:
        case PEEKED_UNQUOTED_NAME:
          return JsonToken.NAME;
    
    

    우 리 는 세 가지 peek 에서 나 온 데이터 형식 을 볼 수 있 습 니 다. 실제 적 으로 Json 의 식별 자 에 대응 할 수 있 습 니 다. 그러면 peek 에서 나 온 기호 유형 (정형 상수) 과 Json 의 식별 자 (JsonToken 매 거 상수) 는 실제 적 으로 다 중 대응 하 는 수치 관계 이기 때문에 혼용 할 수 없습니다. 또한 우 리 는 peek () 에서방법 에서 생 성 된 이 JsonToken 은 Reader 에 기록 되 어 있 지 않 습 니 다. 즉, 데이터 에 대한 분석 에 있어 Reader 는 doPeek 방법 에서 생 성 된 중간 상태 데이터 에 관심 이 있 습 니 다.
    code 2 로 돌아 갑 니 다. 위의 분석 에 따 르 면 reader. peek () 방법 은 실제 적 으로 데이터 변 수 를 초기 화 한 것 임 을 알 고 있 습 니 다. 이 어 from JSon 방법 은 TypeToken. get 의 정적 방법 으로 TypeToken 대상 을 생 성 합 니 다.
    public static TypeToken> get(Type type) {
            return new TypeToken(type);
      }
    

    TypeToken 의 get 방식 은 정적 구조 공장 으로 TypeToken 대상 을 직접 되 돌려 줍 니 다.
    Gson gson = new Gson();
    String strJson = "{name:'david',age:19,room:{roomName:'small',number:1}}";
    User u = gson.fromJson(strJson, User.class);
    
    

    TypeToken. get (User. class) 을 되 돌 릴 때 내부 에 User. class 를 Type 으로 하 는 TypeToken 변 수 를 가지 고 있 습 니 다. TypeToken. get (Type) 을 통 해방법 이 호출 되면 getAdapter 라 는 방법 으로 typeAdapter 대상 을 얻 을 수 있 습 니 다. TypeAdapter 는 Gson 코드 시스템 의 중요 한 부분 으로 진정한 의미 의 전환 작업 을 맡 았 습 니 다. Gson 은 Adapter 관리 에 있어 서 향 원 을 이용 하 였 습 니 다. 분석 류 구 조 는 일정한 시간 소 모 를 가지 고 있 기 때문에 이러한 시간 비용 을 낮 추기 위해 Gson 은 버퍼 를 사용 하여 관리 하 였 습 니 다.매 핑 관계. 아마도 독자 에 게 질문 이 있 을 것 입 니 다. TypeToken. get 방법 은 매번 정적 공장 의 방식 으로 새로운 TypeToken 을 구성 하 는 것 입 니 다. 그러면 cache 에 넣 으 면 cache 의 역할 을 하지 못 합 니까? 답 은: 네. 이 질문 에 대답 하려 면 TypeToken 의 소스 코드 로 돌아 가 야 합 니 다.
    
        @Override
        public final int hashCode() {
            return this.hashCode;
        }
    
        @Override
        public final boolean equals(Object o) {
            return o instanceof TypeToken> && $Gson$Types.equals(type, ((TypeToken>) o).type);
        }
    
    

    위 코드 에 따 르 면 TypeToken 은 같은 hashCode 와 복사 한 equals 방법 으로 서로 다른 대상 의 충돌 문 제 를 해결 합 니 다. 좋 습 니 다. getAdapter 방법 으로 계속 돌아 가 겠 습 니 다.
    // code Gson.getAdapter
    public  TypeAdapter getAdapter(TypeToken type) {
          TypeAdapter> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type);
          if (cached != null) {
              return (TypeAdapter) cached;
          }
    
          Map, FutureTypeAdapter>> threadCalls = calls.get();
          boolean requiresThreadLocalCleanup = false;
          if (threadCalls == null) {
              threadCalls = new HashMap, FutureTypeAdapter>>();
              calls.set(threadCalls);
              requiresThreadLocalCleanup = true;
          }
    
          // the key and value type parameters always agree
          FutureTypeAdapter ongoingCall = (FutureTypeAdapter) threadCalls.get(type);
          if (ongoingCall != null) {
              return ongoingCall;
          }
    
          try {
              FutureTypeAdapter call = new FutureTypeAdapter();
              threadCalls.put(type, call);
    
              for (TypeAdapterFactory factory : factories) {
                  TypeAdapter candidate = factory.create(this, type);
                  if (candidate != null) {
                      call.setDelegate(candidate);
                      typeTokenCache.put(type, candidate);
                      return candidate;
                  }
              }
              throw new IllegalArgumentException("GSON cannot handle " + type);
          } finally {
              threadCalls.remove(type);
    
              if (requiresThreadLocalCleanup) {
                  calls.remove();
              }
          }
      }
    
    

    cache 에 필요 한 어댑터 가 존재 하지 않 을 때, 버퍼 대상 이 존재 하지 않 을 때, Gson 은 맵 대상 을 만들어 라인 에 넣 습 니 다. 또한 Gson 의 Cache 특징 도 발견 할 수 있 습 니 다.
  • Gson 의 Cache 관리 에서 실제 적 으로 2 급 버퍼 를 응용 했다.
  • Gson 대상 내부 에 버퍼 가 있 고 Gson 대상 이 있 는 스 레 드 에 공유 Cache 가 있 습 니 다.
  • 서로 다른 스 레 드 사이 에 서로 다른 cache 를 사용 합 니 다.
  • //    
    FutureTypeAdapter call = new FutureTypeAdapter();
          threadCalls.put(type, call);
          for (TypeAdapterFactory factory : factories) {
            TypeAdapter candidate = factory.create(this, type);
            if (candidate != null) {
              call.setDelegate(candidate);
              typeTokenCache.put(type, candidate);
              return candidate;
            }
          }
    
    

    Adapter 의 구 조 는 하나의 factories 집합 클래스 를 옮 겨 다 니 며 이 루어 집 니 다. 방법 도 간단 합 니 다. Factory 는 들 어 오 는 TypeToken 형식 과 일치 하 는 지, 일치 하 는 지 여 부 를 통 해 대상 을 생 성 합 니 다. 일치 하지 않 으 면 null 로 돌아 갑 니 다. 또한 Adapter 대상 이 생 성 되면 구조 대상 이 우선 순위 가 있 음 을 설명 합 니 다. AFactory 는 규칙 을 통 해 대상 을 생 성 할 수 있 습 니 다. BFactory 는 규칙 을 통 해 대상 을 생 성 할 수 있 습 니 다. 이 럴 때 AFactory 가 BFactory 앞 에 있 으 면 AFactory 를 우선 사용 하여 Adapter 를 구성 합 니 다. factories 의 초기 화 데 이 터 는 Gson 의 구조 기 에 있 습 니 다.
     Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingStrategy,
             final Map> instanceCreators, boolean serializeNulls,
             boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe,
             boolean prettyPrinting, boolean lenient, boolean serializeSpecialFloatingPointValues,
             LongSerializationPolicy longSerializationPolicy,
             List typeAdapterFactories)
    {
         factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);//  JsonElement      
            factories.add(ObjectTypeAdapter.FACTORY);//  Object      
    
            // the excluder must precede all adapters that handle user-defined types
            factories.add(excluder);//             
    
            // user's type adapters
            factories.addAll(typeAdapterFactories);
    
            // type adapters for basic platform types
            factories.add(TypeAdapters.STRING_FACTORY);
            factories.add(TypeAdapters.INTEGER_FACTORY);
            factories.add(TypeAdapters.BOOLEAN_FACTORY);
            factories.add(TypeAdapters.BYTE_FACTORY);
            factories.add(TypeAdapters.SHORT_FACTORY);
            TypeAdapter longAdapter = longAdapter(longSerializationPolicy);
            factories.add(TypeAdapters.newFactory(long.class, Long.class, longAdapter));
            factories.add(TypeAdapters.newFactory(double.class, Double.class,
                    doubleAdapter(serializeSpecialFloatingPointValues)));
            factories.add(TypeAdapters.newFactory(float.class, Float.class,
                    floatAdapter(serializeSpecialFloatingPointValues)));
            factories.add(TypeAdapters.NUMBER_FACTORY);
            factories.add(TypeAdapters.ATOMIC_INTEGER_FACTORY);
            factories.add(TypeAdapters.ATOMIC_BOOLEAN_FACTORY);
            factories.add(TypeAdapters.newFactory(AtomicLong.class, atomicLongAdapter(longAdapter)));
            factories.add(TypeAdapters.newFactory(AtomicLongArray.class, atomicLongArrayAdapter(longAdapter)));
            factories.add(TypeAdapters.ATOMIC_INTEGER_ARRAY_FACTORY);
            factories.add(TypeAdapters.CHARACTER_FACTORY);
            factories.add(TypeAdapters.STRING_BUILDER_FACTORY);
            factories.add(TypeAdapters.STRING_BUFFER_FACTORY);
            factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL));
            factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER));
            factories.add(TypeAdapters.URL_FACTORY);
            factories.add(TypeAdapters.URI_FACTORY);
            factories.add(TypeAdapters.UUID_FACTORY);
            factories.add(TypeAdapters.CURRENCY_FACTORY);
            factories.add(TypeAdapters.LOCALE_FACTORY);
            factories.add(TypeAdapters.INET_ADDRESS_FACTORY);
            factories.add(TypeAdapters.BIT_SET_FACTORY);
            factories.add(DateTypeAdapter.FACTORY);
            factories.add(TypeAdapters.CALENDAR_FACTORY);
            factories.add(TimeTypeAdapter.FACTORY);
            factories.add(SqlDateTypeAdapter.FACTORY);
            factories.add(TypeAdapters.TIMESTAMP_FACTORY);
            factories.add(ArrayTypeAdapter.FACTORY);
            factories.add(TypeAdapters.CLASS_FACTORY);
    
            // type adapters for composite and user-defined types
            factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
            factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
            this.jsonAdapterFactory = new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor);
            factories.add(jsonAdapterFactory);
            factories.add(TypeAdapters.ENUM_FACTORY);
            factories.add(new ReflectiveTypeAdapterFactory(
                    constructorConstructor, fieldNamingStrategy, excluder, jsonAdapterFactory));
    
            this.factories = Collections.unmodifiableList(factories);
    }
    
    

    "User. class 유형 이 들 어 왔 을 때 Gson 은 Reflective TypeAdapter Factory 공장 을 통 해 Adapter 를 생산 하여 JSon 대상 에 적합 합 니 다.
    public  TypeAdapter create(Gson gson, final TypeToken type) {
        Class super T> raw = type.getRawType();
    
        if (!Object.class.isAssignableFrom(raw)) {//      
          return null; // it's a primitive!
        }
    
        ObjectConstructor constructor = constructorConstructor.get(type);//     
        return new Adapter(constructor, getBoundFields(gson, type, raw));
      }
    

    기타
    Gson gson = new GsonBuilder()
            //   null
            .serializeNulls()
            //         ,  2     
            //             
            .setDateFormat("yyyy-MM-dd")
            //         
            .disableInnerClassSerialization()
            //       Json(   )]}'  4   )
            .generateNonExecutableJson()
            //    html  
            .disableHtmlEscaping()
            //     
            .setPrettyPrinting()
            .create();
    

    좋은 웹페이지 즐겨찾기