Utilisation D'Énums lors de l'analyse de JSON avec GSON

ceci est lié à une question précédente que j'ai posée ici plus tôt

parsing JSON à l'aide de Gson

j'essaie d'analyser le même JSON, mais maintenant j'ai changé un peu mes classes.

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

ma classe ressemble maintenant à:

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

ce code jette une exception,

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 

l'exception est compréhensible, parce que selon la solution à ma question précédente, GSON s'attend à ce que les objets Enum soient réellement créés comme

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

mais puisque c'est syntaxiquement impossible, quelles sont les solutions recommandées, solutions de rechange?

86
demandé sur fawaad 2011-11-21 15:46:08

7 réponses

à Partir de la documentation de Gson :

Gson fournit la sérialisation et la désérialisation par défaut pour Enums... Si vous préférez modifier la représentation par défaut, vous pouvez le faire en enregistrant un type d'adaptateur par L'intermédiaire de GsonBuilder.registerTypeAdapter (Type, Objet).

En voici une.

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;
  }
}
45
répondu Programmer Bruce 2016-06-04 16:22:19

je veux étendre un peu NAZIK/user2724653 réponse (pour mon cas). Voici un code Java:

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

dans le fichier json vous avez juste un champ "status": "N", , où N=0,1,2,3 - dépendent des valeurs D'État. Donc c'est tout, GSON fonctionne très bien avec les valeurs pour la classe imbriquée enum . Dans mon cas, j'ai analysé une liste de Items de json array:

List<Item> items = new Gson().<List<Item>>fromJson(json,
                                          new TypeToken<List<Item>>(){}.getType());
229
répondu validcat 2016-12-20 05:35:55

Utiliser l'annotation @SerializedName :

@SerializedName("${title}")
TITLE,
@SerializedName("${description}")
DESCRIPTION
22
répondu Inc 2014-11-13 16:51:49

avec GSON Version 2.2.2 enum sera facilement marshalled et unmarshalled.

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;
  }
}
6
répondu user2601995 2014-11-10 00:31:31

l'extrait suivant supprime la nécessité d'une mention explicite Gson.registerTypeAdapter(...) , en utilisant l'annotation @JsonAdapter(class) , disponible depuis gson 2.3 (voir commentaire 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;
            }
        }
    }
}
2
répondu Wout 2018-03-22 11:14:16

si vous voulez vraiment utiliser la valeur ordinale de L'Enum, vous pouvez enregistrer un type d'adaptateur factory pour outrepasser l'usine par défaut de 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);
        }
    };
}

alors enregistrez l'usine.

Gson gson = new GsonBuilder()
               .registerTypeAdapterFactory(EnumTypeAdapter.ENUM_FACTORY)
               .create();
1
répondu Tom Bollwitt 2018-03-09 17:36:41

utiliser cette méthode

GsonBuilder.enableComplexMapKeySerialization();
-1
répondu Ahamadullah Saikat 2018-08-11 18:29:24