2017-02-18 4 views
0

직렬화에 Gson을 사용하고 있으며 동적으로 필드 이름을 변경하는 데 어려움을 겪고 있습니다. 수업은 다음과 같습니다.Gson : 직렬화에서 특정 필드 키를 변경하는 방법

public class Response<T> 
{ 

    private String status; 
    private String message; 
    private T data; 

    public Response(T data) 
    { 
     this.setData(data); 
    } 

    public String getStatus() { 
     return status; 
    } 

    public void setStatus(String status) { 
     this.status = status; 
    } 

    public String getMessage() { 
     return message; 
    } 

    public void setMessage(String message) { 
     this.message = message; 
    } 

    public T getData() { 
     return data; 
    } 

    public void setData(T data) { 
     this.data = data; 
    }  

} 

자원을 기반으로 필드 이름을 동적으로 변경해야합니다. 이것을 바꿀 방법이 있습니까?

답변

0

당신은 당신이 유형의 안전을 필요가 없습니다 의미보다 필드의 이름을 변경해야하므로 다음이 할 경우 :

Map<String, Object> response = new LinkedHashMap<>(); 
response.put("message", message); 
response.put("status", status); 
response.put(dynamicFieldName, dynamicFieldValue); 
String json = gson.toJson(response); 

당신은 여전히 ​​낮은 수준의 상단에 편의 라이브러리를 래핑 할 수 일반적인 사용 사례를 설명하는 코드

0

그런 경우에 HashMap을 사용하지 않는 이유는 무엇입니까? 데이터 필드 역시 <String, Object> 또는 <Long, Object>의 HashMap에있을 수 GSON 따라서 모든 종류의 수용성 서브 오브젝트를 저장하는 것을

private HashMap<String, String> data; 

public HashMap<String, String> getData() { 
    return this.data; 
} 

public void setDataValue(String key, String value) { 
    data.put(key, value); 
} 

주 구조를 지원.

1

Response 클래스에 응답 객체가지도로 변환되면 무시 될 특수 Gson 주석이있을 수 있으므로지도를 사용하는 것이 최선의 방법이 아닙니다.

는 다음과 같은 간단한 응답 클래스를 가정 :

final class Response<T> { 

    @Expose(serialize = true) 
    final String status = "STATUS"; 

    @Expose(serialize = true) 
    final String message = "MESSAGE"; 

    @Expose(serialize = true) 
    final T data; 

    @Expose(serialize = false, deserialize = false) 
    final String whatever = "WHATEVER"; 

    Response(final T data) { 
     this.data = data; 
    } 

} 

이 응답 단순 또 다른 GSON 주석을 사용하지 않습니다. 동적 필드 이름 변경의 임시 사용 : 주요 트릭은 여기에 중간 JSON 트리를 만들고 동적 속성 이름을 대체되는 것을

final Gson gson = new GsonBuilder() 
     .excludeFieldsWithoutExposeAnnotation() 
     // ... any Gson configuration here ... 
     .create(); 
final Response<List<String>> response = new Response<>(ImmutableList.of("foo", "bar")); 
final JsonElement jsonTree = gson.toJsonTree(response, stringListResponseTypeToken.getType()); 
final JsonObject responseJson = jsonTree.getAsJsonObject(); 
final JsonElement dataPropertyJson = responseJson.get("data"); 
responseJson.remove("data"); 
responseJson.add(response.getClass().getSimpleName(), dataPropertyJson); 
gson.toJson(responseJson, System.out); 

참고. 불행히도이 솔루션에는 중간 JSON 트리가 필요합니다. 또 다른 "Gson-ish"솔루션은 필요할 때마다 응답 개체를 다시 매핑하지 않기 위해 특수 유형 어댑터를 만듭니다. 유형 어댑터 공장과 유형 어댑터로 구현됩니다

final Gson gson = new GsonBuilder() 
     .excludeFieldsWithoutExposeAnnotation() 
     // ... any Gson configuration here ... 
     .registerTypeAdapterFactory(getDynamicPropertyResponseTypeAdapterFactory()) 
     .create(); 
final Response<List<String>> response = new Response<>(ImmutableList.of("foo", "bar")); 
gson.toJson(response, stringListResponseTypeToken.getType(), System.out); 

은 다음과 같습니다

이러한 유형의 어댑터 공장은 처리 클래스 Response 경우 무한 재귀을 피하기 위해 다운 스트림 타입의 어댑터를 선택합니다하는 것으로
final class DynamicPropertyResponseTypeAdapterFactory 
     implements TypeAdapterFactory { 

    private static final TypeAdapterFactory dynamicPropertyResponseTypeAdapterFactory = new DynamicPropertyResponseTypeAdapterFactory(); 

    private DynamicPropertyResponseTypeAdapterFactory() { 
    } 

    static TypeAdapterFactory getDynamicPropertyResponseTypeAdapterFactory() { 
     return dynamicPropertyResponseTypeAdapterFactory; 
    } 

    @Override 
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) { 
     if (Response.class.isAssignableFrom(typeToken.getRawType())) { 
      @SuppressWarnings("unchecked") 
      final TypeAdapter<Response<Object>> delegateTypeAdapter = (TypeAdapter<Response<Object>>) gson.getDelegateAdapter(this, typeToken); 
      @SuppressWarnings("unchecked") 
      final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) getDynamicPropertyResponseJsonTypeAdapter(delegateTypeAdapter, gson); 
      return castTypeAdapter; 
     } 
     return null; 
    } 

} 

하고, 그렇지 않으면 null이 반환되어 Gson이 자체 (비) 직렬화 전략을 사용하게합니다.

final class DynamicPropertyResponseJsonTypeAdapter<T> 
     extends TypeAdapter<Response<T>> { 

    private final TypeAdapter<Response<T>> delegateTypeAdapter; 
    private final Gson gson; 

    private DynamicPropertyResponseJsonTypeAdapter(final TypeAdapter<Response<T>> delegateTypeAdapter, final Gson gson) { 
     this.delegateTypeAdapter = delegateTypeAdapter; 
     this.gson = gson; 
    } 

    static <T> TypeAdapter<Response<T>> getDynamicPropertyResponseJsonTypeAdapter(final TypeAdapter<Response<T>> delegateTypeAdapter, final Gson gson) { 
     return new DynamicPropertyResponseJsonTypeAdapter<>(delegateTypeAdapter, gson); 
    } 

    @Override 
    @SuppressWarnings("resource") 
    public void write(final JsonWriter out, final Response<T> response) 
      throws IOException { 
     if (response == null) { 
      out.nullValue(); 
      return; 
     } 
     final JsonElement jsonTree = delegateTypeAdapter.toJsonTree(response); 
     final JsonObject responseJson = jsonTree.getAsJsonObject(); 
     final JsonElement dataPropertyJson = responseJson.get("data"); 
     responseJson.remove("data"); 
     responseJson.add(response.getClass().getSimpleName(), dataPropertyJson); 
     gson.toJson(responseJson, out); 
    } 

    @Override 
    public Response<T> read(final JsonReader in) { 
     throw new UnsupportedOperationException(); 
    } 

} 

동일하지 아주 싼 트릭 위에서 사용되지만 지금은 Gson 인스턴스의 일부로서 작동합니다.다음과 같이 두 가지 경우의 출력은 :

{ "상태": "STATUS", "메시지": "메시지", "응답"[ "foo에", "바"]}

당신이 고려해야 할 수 있습니다

또 다른 옵션은 다음과 같습니다

  • Response 클래스 추상적 만들기와 이름이 하드 코딩 있어야하는 경우 하위 클래스가 @SerializedName를 통해 자신의 data 필드 이름을 정의하는 데시키는.
  • 구현 코드 ReflectiveTypeAdapterFactory (Gson 소스 코드 참조)을 만들고 JSON 트리를 생성하지 않고 필드 이름을 동적으로 만듭니다.