Использование перечислений при разборе JSON с GSON
это связано с предыдущим вопросом, который я задавал здесь ранее
Я пытаюсь разобрать тот же 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 ожидает, что объекты перечисления будут быть на самом деле быть создан как
${title}("${title}"),
${description}("${description}");
но поскольку это синтаксически невозможно, каковы рекомендуемые решения, обходные пути?
7 ответов:
Gson предоставляет сериализацию и десериализацию по умолчанию для перечислений... Если вы хотите изменить представление по умолчанию, вы можете сделать это, зарегистрировав адаптер типа через 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; } }
Я хочу немного расширить ответ NAZIK / user2724653 (для моего случая). Здесь-это 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 } }в файле JSON у вас есть только поле
"status": "N",, где N=0,1,2,3 - зависит от значения статуса. Вот и все,GSONотлично работает со значениями для вложенныхenumкласса. В моем случае я проанализировал списокItemsСjsonвремя:List<Item> items = new Gson().<List<Item>>fromJson(json, new TypeToken<List<Item>>(){}.getType());
использовать аннотации
@SerializedName:@SerializedName("${title}") TITLE, @SerializedName("${description}") DESCRIPTION
С Gson версия 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; } }
следующий фрагмент кода устраняет необходимость в явном
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; } } } }
Если вы действительно хотите использовать порядковое значение перечисления, вы можете зарегистрировать фабрику адаптера типа для переопределения фабрики по умолчанию 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();
Comments