Retrofit+RxJava 실전 로그(4) - Gson 빈 문자열 문제 해결
이 문제는 내가 Retrofit+Gson을 사용한 이래 가장 큰 구덩이라고 할 수 있다. 그래서 나는 연구 시차가 많지 않아서 원본 코드를 다 봐야 한다.
기괴한 것은 Gson이 빈 문자열을 정형으로 해석할 때'Inavalid double'라는 오류를 보고한 것이다. 원본 코드를 연구한 결과 Gson은 정형으로 해석하는 것을 우선적으로 시도한다. 해석 실패는 오류를 보고하지 않는다. 계속해서 double형으로 해석을 시도한 다음에 실패하면 오류를 보고한다. 그래서'Inavalid double'를 얻었다.
해결 방안: 정형에 대한 해석을 위해 먼저 해석 어댑터를 쓰고
JsonSerializer<Integer>, JsonDeserializer<Integer>
해석 방법을 다시 쓰기를 실현한다. 먼저 String 형식 해석을 시험적으로 사용하고 빈 문자열''과 같으면 0값을 되돌려준다. 그렇지 않으면 정형 해석을 시도하고catch 디지털 형식 이상은 Json 해석 이상 던지기로 전환한다.public class IntegerDefault0Adapter implements JsonSerializer<Integer>, JsonDeserializer<Integer> {
@Override
public Integer deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context)
throws JsonParseException {
try {
if (json.getAsString().equals("")){
return 0;
}
} catch (Exception ignore){
}
try {
return json.getAsInt();
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
@Override
public JsonElement serialize(Integer src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src);
}
}
그리고 GsonBuilder에 어댑터를 형식 Integer와 형식 int에 등록합니다
public static Gson buildGson() {
if (gson == null) {
gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.registerTypeAdapter(Integer.class, new IntegerDefault0Adapter())
.registerTypeAdapter(int.class, new IntegerDefault0Adapter())
.create();
}
return gson;
}
Retrofit를 구축할 때 이 사용자 정의 Gson으로 원본을 대체합니다.
Retrofit = new Retrofit.Builder()
.baseUrl(API_SERVER + "/")
// buildGson Gson
.addConverterFactory(ResponseConverterFactory.create(buildGson()))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(mOkHttpClient)
.build();
이렇게 하면 Gson이 정형 해석을 만났을 때 빈 문자열을 0으로 해석할 수 있다
그리고 리스트의 해석 문제를 같은 방식으로 해결하려고 했는데 상황이 이렇게 간단하지 않을 줄은 몰랐어요.List는 성형처럼 기본적인 유형이 아니기 때문에 List 자체가 데이터 분석을 할 때 범주형을 가져야 하기 때문에 구축할 때 집합 안의 데이터 유형을 정할 수 없다.범용형의 데이터 형식을 정하지 않으면 어댑터를 다시 쓰는 것은 실행할 때 만나는 유형에 따라 각각 조작해야 한다. 이것은 Gson의 작업을 다시 하는 것과 다름없다.또한 원본 코드를 연구한 결과 Gson이 List에 대해 하나의 유형으로 해석하는 것이 아니라 초기화할 때 CollectionTypeAdapterFactory를 가지고 있고 JsonArray 유형의 데이터를 만나면 집합 유형의 해석기를 호출한 다음에 집합에 대응하는 데이터 유형을 맞춘다.한마디로 복잡하고 확장이 잘 안 된다.
연구를 통해 제가 찾은 해결 방안은 주석 방식 @JsonAdapter를 통해 대응하는 어댑터를 지정할 수 있습니다. 우선 순위는 기본 CollectionTypeAdapterFactory보다 높고 GsonBuilder를 통해 전송된 어댑터보다 높습니다.그리고 CollectionTypeAdapterFactory를 복사해서 나오고ListTypeAdapterFactory를 바꿔서 나오세요.
/** * * @JsonAdapter CollectionTypeAdapterFactory */
public final class ListTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
Class<? super T> rawType = typeToken.getRawType();
if (!List.class.isAssignableFrom(rawType)) {
return null;
}
Type elementType = $Gson$Types.getCollectionElementType(type, rawType);
TypeAdapter<?> elementTypeAdapter = gson.getAdapter(TypeToken.get(elementType));
@SuppressWarnings({"unchecked", "rawtypes"}) // create() doesn't define a type parameter
TypeAdapter<T> result = new Adapter(gson, elementType, elementTypeAdapter);
return result;
}
private static final class Adapter<E> extends TypeAdapter<List<E>> {
private final TypeAdapter<E> elementTypeAdapter;
public Adapter(Gson context, Type elementType,
TypeAdapter<E> elementTypeAdapter) {
this.elementTypeAdapter = new TypeAdapterRuntimeTypeWrapper<E>(
context, elementTypeAdapter, elementType);
}
// ,
public List<E> read(JsonReader in) throws IOException {
//null null
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
//
List<E> list = new ArrayList<>();
try {
in.beginArray();
while (in.hasNext()) {
E instance = elementTypeAdapter.read(in);
list.add(instance);
}
in.endArray();
//
} catch (IllegalStateException e){ // , BEGIN_ARRAY
// , ,
// , ,
if (!"".equals(in.nextString())){
throw e;
}
}
return list;
}
public void write(JsonWriter out, List<E> list) throws IOException {
if (list == null) {
out.nullValue();
return;
}
out.beginArray();
for (E element : list) {
elementTypeAdapter.write(out, element);
}
out.endArray();
}
}
}
마지막으로 모델 클래스에서 메모 지정
public class UserListModel{
@JsonAdapter(ListTypeAdapterFactory.class)
List<UserProfileModel> users;
}
이렇게 하면 Gson은 빈 문자열을 빈 목록으로 해석할 수 있다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin의 기초 - 2부지난 글에서는 Kotlin이 무엇인지, Kotlin의 특징, Kotlin에서 변수 및 데이터 유형을 선언하는 방법과 같은 Kotlin의 기본 개념에 대해 배웠습니다. 유형 변환은 데이터 변수의 한 유형을 다른 데이터...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.