TypeAdapter का उपयोग करके किसी ऑब्जेक्ट में एक चर (कई के) के लिए कस्टम सेरलाइज़र


96

मैंने एक कस्टम टाइप एडाप्टर का उपयोग करने के बहुत सारे सरल उदाहरण देखे हैं। सबसे ज्यादा मददगार रहा है Class TypeAdapter<T>। लेकिन इसने मेरे सवाल का जवाब अभी तक नहीं दिया है।

मैं ऑब्जेक्ट में किसी एक क्षेत्र के क्रमांकन को अनुकूलित करना चाहता हूं और डिफ़ॉल्ट गसन तंत्र को बाकी का ध्यान रखना चाहता हूं।

चर्चा के उद्देश्यों के लिए, हम इस वर्ग परिभाषा का उपयोग उस वस्तु के वर्ग के रूप में कर सकते हैं जिसे मैं क्रमबद्ध करना चाहता हूं। मैं पहले दो वर्ग के सदस्यों के साथ-साथ बेस क्लास के सभी उजागर सदस्यों को सीरियल देना चाहता हूं, और मैं नीचे दिखाए गए तीसरे और अंतिम वर्ग के सदस्य के लिए कस्टम क्रमांकन करना चाहता हूं।

public class MyClass extends SomeClass {

@Expose private HashMap<String, MyObject1> lists;
@Expose private HashMap<String, MyObject2> sources;
private LinkedHashMap<String, SomeClass> customSerializeThis;
    [snip]
}

जवाबों:


131

यह एक महान प्रश्न है क्योंकि यह कुछ को अलग करता है जो आसान होना चाहिए लेकिन वास्तव में बहुत सारे कोड की आवश्यकता होती है।

शुरू करने के लिए, एक सार लिखें जो TypeAdapterFactoryआपको आउटगोइंग डेटा को संशोधित करने के लिए हुक देता है। यह उदाहरण Gson 2.2 में एक नए एपीआई का उपयोग करता है getDelegateAdapter(), जो आपको एडेप्टर को देखने की अनुमति देता है जिसे Gson डिफ़ॉल्ट रूप से उपयोग करेगा। यदि आप मानक व्यवहार को मोड़ना चाहते हैं तो प्रतिनिधि एडेप्टर बेहद उपयोगी हैं। और पूर्ण कस्टम प्रकार एडाप्टर के विपरीत, वे फ़ील्ड जोड़ते और निकालते समय स्वचालित रूप से अद्यतित रहेंगे।

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()उपविधि को उस पेड़ को अनुकूलित करने की अनुमति देने के लिए कॉल करता है । इसी तरह के साथ deserialization के लिए afterRead()

आगे हम इसे विशिष्ट MyClassउदाहरण के लिए उप-वर्ग करते हैं । यह स्पष्ट करने के लिए कि मैं सीरियल के आकार का सिंथेटिक गुण जोड़ूंगा, जब इसे सीरियल किया जाएगा। और समरूपता के लिए मैं इसे हटा दूँगा जब यह deserialized है। व्यवहार में यह कोई अनुकूलन हो सकता है।

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 के नए टाइप एडेप्टर और टाइप एडेप्टरफैक्टरी प्रकार बेहद शक्तिशाली हैं, लेकिन वे अमूर्त भी हैं और प्रभावी रूप से उपयोग करने के लिए अभ्यास करते हैं। उम्मीद है कि आपको यह उदाहरण उपयोगी लगेगा!


@ जेसे धन्यवाद! मैं आपकी मदद के बिना यह पता लगाना कभी नहीं होगा!
माउंटेनएक्स

मैं new MyClassTypeAdapterFactory()निजी ctor के साथ तात्कालिकता में सक्षम नहीं था ...
MountainX

आह, इसके बारे में क्षमा करें। मैंने यह सब एक फाइल में किया।
जेसी विल्सन

7
वह मैकेनिम (पहले और बाद में) GSON कोर का हिस्सा होना चाहिए। धन्यवाद!
मेलानी

2
मैं पारस्परिक संदर्भ के कारण अनंत लूप से बचने के लिए टाइप एडेप्टर का उपयोग कर रहा हूं .. यह एक महान तंत्र है धन्यवाद @Jesse हालांकि मैं पूछना चाहता हूं कि क्या आपके पास इस तंत्र के साथ समान प्रभाव प्राप्त करने का विचार है .. मेरे पास चीजें हैं लेकिन मैं आपकी राय सुनना चाहता हूँ .. धन्यवाद!
मोहम्मद आर। एल-खौदरी

16

इसके लिए एक और तरीका है। जैसा कि जेसी विल्सन कहते हैं, यह आसान माना जाता है। और लगता है क्या, यह है आसान!

यदि आप लागू करते हैं JsonSerializerऔर JsonDeserializerअपने प्रकार के लिए, आप अपने इच्छित भागों को संभाल सकते हैं और बहुत कम कोड के साथ बाकी सब के लिए Gson को सौंप सकते हैं । मैं सुविधा के लिए नीचे एक और प्रश्न पर @ धारणा के उत्तर से उद्धृत कर रहा हूं , अधिक विवरण के लिए वह उत्तर देखें:

इस मामले में इसका बेहतर उपयोग एक JsonSerializerविरोधाभासी के रूप में किया जा सकता है 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;
    }
}

इसका मुख्य लाभ (जटिल वर्कअराउंड से बचने के अलावा) यह है कि आप अभी भी अन्य प्रकार के एडेप्टर और कस्टम धारावाहिकों का लाभ उठा सकते हैं जो मुख्य संदर्भ में पंजीकृत हो सकते हैं। ध्यान दें कि धारावाहिक और पंजीकरणकर्ताओं का पंजीकरण एक ही कोड का उपयोग करता है।

हालांकि, मैं यह स्वीकार करूंगा कि यदि आप अक्सर अपने जावा ऑब्जेक्ट में फ़ील्ड को संशोधित करने जा रहे हैं तो जेसी का दृष्टिकोण बेहतर दिखता है। यह आसानी से उपयोग बनाम लचीलेपन का एक व्यापार बंद है, अपनी पिक ले लो।


1
यह अन्य सभी क्षेत्रों valueको आगाह करने में विफल रहता है
वेस्ले

10

मेरे सहयोगी ने @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/Json_cape.html

उदाहरण:

 private static final class Gadget {
   @JsonAdapter(UserJsonAdapter2.class)
   final User user;
   Gadget(User user) {
       this.user = user;
   }
 }

1
यह मेरे उपयोग के मामले के लिए बहुत अच्छा काम करता है। बहुत बहुत धन्यवाद।
नोकलोक

1
यहाँ एक WebArchive लिंक दिया गया है क्योंकि मूल अब मृत हो गया है: web.archive.org/web/20180119143212/https://google.github.io/…
फ्लोटिंग सनफिश
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.