Usar Enums mientras analiza JSON con GSON
Esto está relacionado con una pregunta anterior que hice aquí antes
Estoy tratando de analizar el mismo JSON, pero ahora he cambiado un poco mis clases.
{
"lower": 20,
"upper": 40,
"delimiter": " ",
"scope": ["${title}"]
}
Mi clase ahora se ve como:
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;
}
}
Este código lanza una excepción,
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
La excepción es comprensible, porque según la solución a mi pregunta anterior, GSON espera que los objetos Enum se creen realmente como
${title}("${title}"),
${description}("${description}");
Pero dado que esto es sintácticamente imposible, ¿cuáles son las soluciones recomendadas, soluciones alternativas?
7 answers
De la documentación para Gson :
Gson proporciona serialización y deserialización predeterminadas para Enums... Si prefiere cambiar la representación predeterminada, puede hacerlo registrando un adaptador de tipo a través de GsonBuilder.registerTypeAdapter (Tipo, Objeto).
El siguiente es uno de esos enfoques.
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;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-04 16:22:19
Quiero expandir un poco la respuesta NAZIK/user2724653 (para mi caso). Aquí hay un código 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
}
}
En el archivo json solo tiene un campo "status": "N",
, donde N=0,1,2,3 - depende de los valores de Estado. Así que eso es todo, GSON
funciona bien con los valores para la clase anidada enum
. En mi caso he analizado una lista de Items
desde json
matriz:
List<Item> items = new Gson().<List<Item>>fromJson(json,
new TypeToken<List<Item>>(){}.getType());
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-20 05:35:55
Use anotación @SerializedName
:
@SerializedName("${title}")
TITLE,
@SerializedName("${description}")
DESCRIPTION
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-13 16:51:49
Con GSON versión 2.2.2 enum será marshalled y sin shalled fácilmente.
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;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-10 00:31:31
El siguiente fragmento elimina la necesidad de Gson.registerTypeAdapter(...)
explícito, utilizando la anotación @JsonAdapter(class)
, disponible desde Gson 2.3 (ver comentario 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;
}
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-22 11:14:16
Si realmente desea utilizar el valor ordinal de la enumeración, puede registrar una fábrica de adaptador de tipo para anular la fábrica predeterminada 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);
}
};
}
Entonces simplemente registre la fábrica.
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(EnumTypeAdapter.ENUM_FACTORY)
.create();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-09 17:36:41
Utilice este método
GsonBuilder.enableComplexMapKeySerialization();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-11 18:29:24