JSON को GSON के साथ पार्स करते समय Enums का उपयोग करना


119

यह एक पिछले प्रश्न से संबंधित है जो मैंने यहां पहले पूछा था

GSON का उपयोग करके JSON पार्स कर रहा है

मैं उसी JSON को पार्स करने की कोशिश कर रहा हूं, लेकिन अब मैंने अपनी कक्षाओं को थोड़ा बदल दिया है।

{
    "lower": 20,
    "upper": 40,
    "delimiter": " ",
    "scope": ["${title}"]
}

मेरा वर्ग अब जैसा दिखता है:

public class TruncateElement {

   private int lower;
   private int upper;
   private String delimiter;
   private List<AttributeScope> scope;

   // getters and setters
}


public enum AttributeScope {

    TITLE("${title}"),
    DESCRIPTION("${description}"),

    private String scope;

    AttributeScope(String scope) {
        this.scope = scope;
    }

    public String getScope() {
        return this.scope;
    }
}

यह कोड एक अपवाद फेंकता है,

com.google.gson.JsonParseException: The JsonDeserializer EnumTypeAdapter failed to deserialized json object "${title}" given the type class com.amazon.seo.attribute.template.parse.data.AttributeScope
at 

अपवाद समझ में आता है, क्योंकि मेरे पिछले प्रश्न के समाधान के अनुसार, GSON को उम्मीद है कि Enum वस्तुओं को वास्तव में बनाया जाएगा

${title}("${title}"),
${description}("${description}");

लेकिन चूंकि यह कृत्रिम रूप से असंभव है, इसलिए अनुशंसित समाधान, वर्कअराउंड क्या हैं?

जवाबों:


57

से Gson के लिए दस्तावेज़ :

Gson Enums के लिए डिफ़ॉल्ट क्रमांकन और डीरिएलाइज़ेशन प्रदान करता है ... यदि आप डिफ़ॉल्ट प्रतिनिधित्व को बदलना पसंद करेंगे, तो आप GsonBuilder.registerTypeAdapter (प्रकार, ऑब्जेक्ट) के माध्यम से एक प्रकार का एडाप्टर दर्ज करके ऐसा कर सकते हैं।

निम्नलिखित एक ऐसा दृष्टिकोण है।

import java.io.FileReader;
import java.lang.reflect.Type;
import java.util.List;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;

public class GsonFoo
{
  public static void main(String[] args) throws Exception
  {
    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(AttributeScope.class, new AttributeScopeDeserializer());
    Gson gson = gsonBuilder.create();

    TruncateElement element = gson.fromJson(new FileReader("input.json"), TruncateElement.class);

    System.out.println(element.lower);
    System.out.println(element.upper);
    System.out.println(element.delimiter);
    System.out.println(element.scope.get(0));
  }
}

class AttributeScopeDeserializer implements JsonDeserializer<AttributeScope>
{
  @Override
  public AttributeScope deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
      throws JsonParseException
  {
    AttributeScope[] scopes = AttributeScope.values();
    for (AttributeScope scope : scopes)
    {
      if (scope.scope.equals(json.getAsString()))
        return scope;
    }
    return null;
  }
}

class TruncateElement
{
  int lower;
  int upper;
  String delimiter;
  List<AttributeScope> scope;
}

enum AttributeScope
{
  TITLE("${title}"), DESCRIPTION("${description}");

  String scope;

  AttributeScope(String scope)
  {
    this.scope = scope;
  }
}

310

मैं थोड़ा सा NAZIK / user2724653 उत्तर (मेरे मामले के लिए) का विस्तार करना चाहता हूं। यहाँ एक जावा कोड है:

public class Item {
    @SerializedName("status")
    private Status currentState = null;

    // other fields, getters, setters, constructor and other code...

    public enum Status {
        @SerializedName("0")
        BUY,
        @SerializedName("1")
        DOWNLOAD,
        @SerializedName("2")
        DOWNLOADING,
        @SerializedName("3")
        OPEN
     }
}

json फ़ाइल में आपके पास बस एक फ़ील्ड है "status": "N",, जहाँ N = 0,1,2,3 - Status मान पर निर्भर करता है। तो यह सब है, GSONनेस्टेड enumक्लास के लिए मूल्यों के साथ ठीक काम करता है । मेरे मामले में मैंने सरणी Itemsसे एक सूची प्राप्त की है json:

List<Item> items = new Gson().<List<Item>>fromJson(json,
                                          new TypeToken<List<Item>>(){}.getType());

28
यह उत्तर पूरी तरह से सब कुछ हल करता है, टाइप एडेप्टर की कोई आवश्यकता नहीं है!
लीना ब्रू

4
जब मैं ऐसा करता हूं, तो रेट्रोफिट / गॉन के साथ, एनम वैल्यूज के सीरीअलाइज्डनाम में अतिरिक्त उद्धरण चिह्न जोड़े जाते हैं। सर्वर वास्तव में प्राप्त करता है "1", उदाहरण के लिए, बस के बजाय 1...
मैथ्यू Housser

17
अगर स्टेटस 5 वाला जस्सन आ जाएगा तो क्या होगा? क्या डिफ़ॉल्ट मान को परिभाषित करने का कोई तरीका है?
दिमित्रीबोरोडिन

8
@DmitryBorodin यदि JSON का मान किसी से मेल नहीं खाता है, SerializedNameतो Enum डिफ़ॉल्ट रूप से होगा null। अज्ञात स्थिति के डिफ़ॉल्ट व्यवहार को एक आवरण वर्ग में संभाला जा सकता है। यदि आपको इसके अलावा "अज्ञात" के लिए एक प्रतिनिधित्व की आवश्यकता है, nullतो आपको एक कस्टम डेज़राइज़र या टाइप एडेप्टर लिखने की आवश्यकता होगी।
एफ पर पीटर एफ

32

एनोटेशन का उपयोग करें @SerializedName:

@SerializedName("${title}")
TITLE,
@SerializedName("${description}")
DESCRIPTION

9

जीएसओएन संस्करण के साथ 2.2.2 ईनम को मार्शेल्ड और आसानी से अनमर्श किया जाएगा।

import com.google.gson.annotations.SerializedName;

enum AttributeScope
{
  @SerializedName("${title}")
  TITLE("${title}"),

  @SerializedName("${description}")
  DESCRIPTION("${description}");

  private String scope;

  AttributeScope(String scope)
  {
    this.scope = scope;
  }

  public String getScope() {
    return scope;
  }
}

8

निम्नलिखित स्निपेट एनोटेशन Gson.registerTypeAdapter(...)का उपयोग करते हुए स्पष्ट की आवश्यकता को हटाता है , जो @JsonAdapter(class)Gson 2.3 के बाद से उपलब्ध है (टिप्पणी pm_labs देखें )।

@JsonAdapter(Level.Serializer.class)
public enum Level {
    WTF(0),
    ERROR(1),
    WARNING(2),
    INFO(3),
    DEBUG(4),
    VERBOSE(5);

    int levelCode;

    Level(int levelCode) {
        this.levelCode = levelCode;
    }

    static Level getLevelByCode(int levelCode) {
        for (Level level : values())
            if (level.levelCode == levelCode) return level;
        return INFO;
    }

    static class Serializer implements JsonSerializer<Level>, JsonDeserializer<Level> {
        @Override
        public JsonElement serialize(Level src, Type typeOfSrc, JsonSerializationContext context) {
            return context.serialize(src.levelCode);
        }

        @Override
        public Level deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
            try {
                return getLevelByCode(json.getAsNumber().intValue());
            } catch (JsonParseException e) {
                return INFO;
            }
        }
    }
}

1
कृपया ध्यान दें कि यह एनोटेशन केवल उपलब्ध संस्करण 2.3 है: google.github.io/gson/apidocs/index.html?com/google/gson/…
pm_labs

3
अपने प्रोफ़ेसर कॉन्फ़िगरेशन में अपने सीरियलाइज़र / डिसेरिएलाइज़र-क्लासेस जोड़ने के लिए सावधान रहें, क्योंकि उन्हें हटाया जा सकता है (यह मेरे लिए हुआ)
TormundThunderfist

2

यदि आप वास्तव में Enum के क्रमिक मूल्य का उपयोग करना चाहते हैं तो आप Gson के डिफ़ॉल्ट कारखाने को ओवरराइड करने के लिए एक प्रकार का एडाप्टर कारखाना पंजीकृत कर सकते हैं।

public class EnumTypeAdapter <T extends Enum<T>> extends TypeAdapter<T> {
    private final Map<Integer, T> nameToConstant = new HashMap<>();
    private final Map<T, Integer> constantToName = new HashMap<>();

    public EnumTypeAdapter(Class<T> classOfT) {
        for (T constant : classOfT.getEnumConstants()) {
            Integer name = constant.ordinal();
            nameToConstant.put(name, constant);
            constantToName.put(constant, name);
        }
    }
    @Override public T read(JsonReader in) throws IOException {
        if (in.peek() == JsonToken.NULL) {
            in.nextNull();
            return null;
        }
        return nameToConstant.get(in.nextInt());
    }

    @Override public void write(JsonWriter out, T value) throws IOException {
        out.value(value == null ? null : constantToName.get(value));
    }

    public static final TypeAdapterFactory ENUM_FACTORY = new TypeAdapterFactory() {
        @SuppressWarnings({"rawtypes", "unchecked"})
        @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
            Class<? super T> rawType = typeToken.getRawType();
            if (!Enum.class.isAssignableFrom(rawType) || rawType == Enum.class) {
                return null;
            }
            if (!rawType.isEnum()) {
                rawType = rawType.getSuperclass(); // handle anonymous subclasses
            }
            return (TypeAdapter<T>) new EnumTypeAdapter(rawType);
        }
    };
}

फिर सिर्फ फैक्ट्री रजिस्टर करें।

Gson gson = new GsonBuilder()
               .registerTypeAdapterFactory(EnumTypeAdapter.ENUM_FACTORY)
               .create();

0

इस विधि का उपयोग करें

GsonBuilder.enableComplexMapKeySerialization();

3
हालांकि यह कोड प्रश्न का उत्तर दे सकता है, लेकिन समस्या को हल करने के तरीके के बारे में अतिरिक्त संदर्भ प्रदान करता है और यह समस्या को हल करता है ताकि उत्तर के दीर्घकालिक मूल्य में सुधार हो सके।
निक 3500

के रूप में 2.8.5 के रूप में यह आवश्यक है कि आप
किम के
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.