Android

[android] retrofit2 JSONP 데이터 파싱

박진만 2019. 7. 9. 16:31
반응형

구글 API 적용 중 일반 JSON 이 아닌 JSONP 형식의 데이터 파싱할 상황 발생

GsonConverterFactory 적용하면 JSON 형식이 아니라며 오류 발생함

일반적인 JSON 형식

{
  "cursor": {
    "currentPageIndex": 0,
    "estimatedResultCount": "0"    
  },
  "context": {
    "title": "aaa",
    "total_results": "0"
  },  
  "google": {
    "url": "https://www.xxx.com?client=xxxxxxxxx"
  }
}

JSONP 형식 : callback 함수명이 감싸고 있는 형태

google({
  "cursor": {
    "currentPageIndex": 0,
    "estimatedResultCount": "0"    
  },
  "context": {
    "title": "aaa",
    "total_results": "0"
  },  
  "google": {
    "url": "https://www.xxx.com?client=xxxxxxxxx"
  }
});

정상적으로 파싱 하려면 콜백 관련 문자열은 제거해줘야 함

커스텀 컨버터 생성 후 레트로핏에 적용

1. GsonPConverterFactory.java 생성

import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;

public final class GsonPConverterFactory extends Converter.Factory {

    Gson gson;

    public GsonPConverterFactory(Gson gson) {
        if (gson == null) throw new NullPointerException("gson == null");
        this.gson = gson;
    }

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new GsonPResponseBodyConverter%3C%3E(gson, adapter);
    }

    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type,
        Annotation[] parameterAnnotations,
        Annotation[] methodAnnotations,
        Retrofit retrofit) {
        return null;
    }
}

2. GsonPResponseBodyConverter.java 생성

import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;

import java.io.IOException;
import java.io.Reader;

import okhttp3.ResponseBody;
import retrofit2.Converter;

final public class GsonPResponseBodyConverter implements Converter<ResponseBody, T> {

    private final Gson gson;
    private final TypeAdapter adapter;

    GsonPResponseBodyConverter(Gson gson, TypeAdapter adapter) {
        this.gson = gson;
        this.adapter = adapter;
    }

    @Override
    public T convert(ResponseBody value) throws IOException {
        Reader reader = value.charStream();
        int item = reader.read();
        while(item != '(' && item != -1) {
            item = reader.read();
        }

        JsonReader jsonReader = gson.newJsonReader(reader);

        try {
            return adapter.read(jsonReader);
        } finally {
            reader.close();
        }
    }
}

3. 레트로핏 적용

gson = new GsonBuilder()
    .setLenient()
    .create();

retrofit = new Retrofit.Builder()
    .baseUrl(url)
    .addConverterFactory(new GsonPConverterFactory(gson))
    .build();
반응형