Использование перечислений при разборе JSON с GSON



это связано с предыдущим вопросом, который я задавал здесь ранее



разбор 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}");


но поскольку это синтаксически невозможно, каковы рекомендуемые решения, обходные пути?

579   7  

7 ответов:

С документация для Gson:

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

использовать этот метод

GsonBuilder.enableComplexMapKeySerialization();

Comments

    Ничего не найдено.