사용자 지정 TypeAdapter를 사용하는 간단한 예제를 많이 보았습니다. 가장 도움이되는 것은 Class TypeAdapter<T>
. 그러나 그것은 아직 내 질문에 대한 답을 얻지 못했습니다.
객체에서 단일 필드의 직렬화를 사용자 정의하고 기본 Gson 메커니즘이 나머지를 처리하도록하고 싶습니다.
토론 목적으로이 클래스 정의를 직렬화하려는 객체의 클래스로 사용할 수 있습니다. Gson이 처음 두 클래스 멤버와 기본 클래스의 모든 노출 된 멤버를 직렬화하도록하고, 아래 표시된 세 번째 및 마지막 클래스 멤버에 대해 사용자 지정 직렬화를 수행하고 싶습니다.
public class MyClass extends SomeClass {
@Expose private HashMap<String, MyObject1> lists;
@Expose private HashMap<String, MyObject2> sources;
private LinkedHashMap<String, SomeClass> customSerializeThis;
[snip]
}
답변
이것은 쉽지만 실제로 많은 코드가 필요한 것을 분리하기 때문에 좋은 질문입니다.
시작하려면 TypeAdapterFactory
나가는 데이터를 수정할 수있는 후크를 제공 하는 초록 을 작성하십시오 . 이 예에서는 getDelegateAdapter()
Gson이 기본적으로 사용할 어댑터를 조회 할 수 있도록하는 Gson 2.2의 새 API를 사용합니다. 델리게이트 어댑터는 표준 동작을 조정하려는 경우 매우 편리합니다. 또한 전체 사용자 지정 유형 어댑터와 달리 필드를 추가 및 제거 할 때 자동으로 최신 상태로 유지됩니다.
public abstract class CustomizedTypeAdapterFactory<C>
implements TypeAdapterFactory {
private final Class<C> customizedClass;
public CustomizedTypeAdapterFactory(Class<C> customizedClass) {
this.customizedClass = customizedClass;
}
@SuppressWarnings("unchecked") // we use a runtime check to guarantee that 'C' and 'T' are equal
public final <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
return type.getRawType() == customizedClass
? (TypeAdapter<T>) customizeMyClassAdapter(gson, (TypeToken<C>) type)
: null;
}
private TypeAdapter<C> customizeMyClassAdapter(Gson gson, TypeToken<C> type) {
final TypeAdapter<C> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<C>() {
@Override public void write(JsonWriter out, C value) throws IOException {
JsonElement tree = delegate.toJsonTree(value);
beforeWrite(value, tree);
elementAdapter.write(out, tree);
}
@Override public C read(JsonReader in) throws IOException {
JsonElement tree = elementAdapter.read(in);
afterRead(tree);
return delegate.fromJsonTree(tree);
}
};
}
/**
* Override this to muck with {@code toSerialize} before it is written to
* the outgoing JSON stream.
*/
protected void beforeWrite(C source, JsonElement toSerialize) {
}
/**
* Override this to muck with {@code deserialized} before it parsed into
* the application type.
*/
protected void afterRead(JsonElement deserialized) {
}
}
위의 클래스는 기본 직렬화를 사용하여 JSON 트리 (로 JsonElement
표시됨)를 가져온 다음 후크 메서드 beforeWrite()
를 호출 하여 하위 클래스가 해당 트리를 사용자 지정할 수 있도록합니다. .NET을 사용한 역 직렬화와 유사합니다 afterRead()
.
다음으로 특정 MyClass
예제를 위해 이것을 하위 클래스로 만듭니다. 설명을 위해 맵이 직렬화 될 때 ‘size’라는 합성 속성을 맵에 추가합니다. 대칭을 위해 역 직렬화 될 때 제거합니다. 실제로 이것은 모든 사용자 정의가 될 수 있습니다.
private class MyClassTypeAdapterFactory extends CustomizedTypeAdapterFactory<MyClass> {
private MyClassTypeAdapterFactory() {
super(MyClass.class);
}
@Override protected void beforeWrite(MyClass source, JsonElement toSerialize) {
JsonObject custom = toSerialize.getAsJsonObject().get("custom").getAsJsonObject();
custom.add("size", new JsonPrimitive(custom.entrySet().size()));
}
@Override protected void afterRead(JsonElement deserialized) {
JsonObject custom = deserialized.getAsJsonObject().get("custom").getAsJsonObject();
custom.remove("size");
}
}
마지막으로 Gson
새로운 유형의 어댑터를 사용 하는 사용자 정의 인스턴스를 만들어 모든 것을 통합합니다.
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new MyClassTypeAdapterFactory())
.create();
Gson의 새로운 TypeAdapter 및 TypeAdapterFactory 유형은 매우 강력하지만 추상적이고 효과적으로 사용하기 위해 연습을합니다. 이 예제가 유용하기를 바랍니다.
답변
이것에 대한 또 다른 접근 방식이 있습니다. Jesse Wilson이 말했듯이 이것은 쉬운 일입니다. 그리고 그것은 무엇을 생각 입니다 쉽게!
당신이 구현 JsonSerializer
하고 JsonDeserializer
당신의 유형에 대해, 당신은 아주 적은 코드로 당신이 원하는 부분을 처리하고 다른 모든 것을 Gson에 위임 할 수 있습니다 . 편의를 위해 아래 다른 질문에 대한 @Perception의 답변을 인용하고 있습니다 . 자세한 내용은 해당 답변을 참조하십시오.
이 경우 serializer가 직렬화 컨텍스트에 액세스 할 수있는 간단한 이유 때문에
JsonSerializer
a 대신 a를 사용하는 것이 좋습니다TypeAdapter
.public class PairSerializer implements JsonSerializer<Pair> { @Override public JsonElement serialize(final Pair value, final Type type, final JsonSerializationContext context) { final JsonObject jsonObj = new JsonObject(); jsonObj.add("first", context.serialize(value.getFirst())); jsonObj.add("second", context.serialize(value.getSecond())); return jsonObj; } }
이것의 주요 이점 (복잡한 해결 방법을 피하는 것 외에)은 기본 컨텍스트에 등록되었을 수있는 다른 유형 어댑터 및 사용자 지정 serializer를 계속 활용할 수 있다는 것입니다. serializer 및 어댑터 등록은 정확히 동일한 코드를 사용합니다.
그러나 Java 객체의 필드를 자주 수정하는 경우 Jesse의 접근 방식이 더 좋아 보인다는 것을 인정합니다. 사용 용이성과 유연성의 절충안입니다. 선택하십시오.
답변
제 동료도 @JsonAdapter
주석 사용에 대해 언급했습니다.
https://google.github.io/gson/apidocs/com/google/gson/annotations/JsonAdapter.html
페이지는 https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/annotations/JsonAdapter.html 로 이동되었습니다.
예:
private static final class Gadget {
@JsonAdapter(UserJsonAdapter2.class)
final User user;
Gadget(User user) {
this.user = user;
}
}