JSON'u GSON ile ayrıştırırken Enums kullanma


119

Bu, daha önce burada sorduğum bir önceki soruyla ilgili

Gson kullanarak JSON ayrıştırma

Aynı JSON'u ayrıştırmaya çalışıyorum ama şimdi sınıflarımı biraz değiştirdim.

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

Sınıfım şimdi şöyle görünüyor:

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;
    }
}

Bu kod bir istisna atar,

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 

İstisna anlaşılabilir, çünkü önceki sorumun çözümüne göre GSON, Enum nesnelerinin şu şekilde oluşturulmasını bekliyor:

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

Ancak bu sözdizimsel olarak imkansız olduğundan, önerilen çözümler nelerdir, geçici çözümler nelerdir?

Yanıtlar:


57

Gönderen GSON belgelerine :

Gson, Enums için varsayılan serileştirme ve seriyi kaldırma sağlar ... Varsayılan gösterimi değiştirmeyi tercih ederseniz, bunu GsonBuilder.registerTypeAdapter (Type, Object) aracılığıyla bir tür bağdaştırıcısı kaydederek yapabilirsiniz.

Aşağıda böyle bir yaklaşım var.

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;
  }
}

311

Biraz NAZIK / user2724653 cevabını genişletmek istiyorum (benim durumum için). İşte bir Java kodu:

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 dosyasında sadece bir alanınız vardır "status": "N",, burada N = 0,1,2,3 - Durum değerlerine bağlıdır. Hepsi bu kadar, GSONiç içe geçmiş enumsınıfın değerleriyle iyi çalışır . Benim durumumda ben bir listesini ayrıştırılır ettik Itemsgelen jsondizinin:

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

28
Bu cevap her şeyi mükemmel bir şekilde çözer, tip adaptörlerine gerek yoktur!
Lena Bru

4
Bunu Retrofit / Gson ile yaptığımda, enum değerlerinin SerializedName değerlerine fazladan tırnak işareti eklendi. Sunucu "1", örneğin basitçe 1...
Matthew Housser

17
Durum 5 olan json gelirse ne olacak? Varsayılan değeri tanımlamanın herhangi bir yolu var mı?
DmitryBorodin

8
@DmitryBorodin JSON'dan gelen değer herhangi biriyle eşleşmezse SerializedName, enum varsayılan olarak null. Bilinmeyen durumun varsayılan davranışı bir sarmalayıcı sınıfında ele alınabilir. Bununla birlikte, "bilinmeyen" için başka bir gösterime ihtiyacınız nullvarsa, özel bir seri çözümleyici veya tip adaptörü yazmanız gerekecektir.
Peter F

32

Ek açıklama kullanın @SerializedName:

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

9

GSON sürüm 2.2.2 ile numaralandırma kolayca sıralanacak ve sıralanacaktır.

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

Aşağıdaki kod parçası, Gson 2.3'ten beri mevcut Gson.registerTypeAdapter(...)olan @JsonAdapter(class)ek açıklamayı kullanarak açıkça ifade etme ihtiyacını ortadan kaldırır (bkz . 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
Lütfen bu ek açıklamanın yalnızca 2.3 sürümünden itibaren kullanılabildiğini unutmayın: google.github.io/gson/apidocs/index.html?com/google/gson/…
pm_labs

3
serileştirici / seri kaldırıcı sınıflarınızı proguard yapılandırmanıza eklemeye dikkat edin, çünkü bunlar kaldırılabilir (benim için oldu)
TormundThunderfist

2

Enum'un sıra değerini gerçekten kullanmak istiyorsanız, Gson'ın varsayılan fabrikasını geçersiz kılmak için bir tür adaptör fabrikası kaydedebilirsiniz.

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);
        }
    };
}

Ardından fabrikayı kaydettirin.

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

0

bu yöntemi kullan

GsonBuilder.enableComplexMapKeySerialization();

3
Bu kod soruyu yanıtlayabilirken, sorunun nasıl ve / veya neden çözüldüğüne ilişkin ek içerik sağlamak, yanıtın uzun vadeli değerini artıracaktır.
Nic3500

gson 2.8.5'ten itibaren bu, anahtar olarak kullanmak istediğiniz numaralandırmalarda SerializedName ek açıklamalarını kullanmak için gereklidir
vazor
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.