GSON десериализует значение ключа для пользовательского объекта

Мне нужно десериализовать json, который является массивом значений date/long. Вот пример возвращенного JSON:

[{"2011-04-30T00:00:00-07:00":100}, {"2011-04-29T00:00:00-07:00":200}]

Используя GSON, я могу десериализовать это на List<Map<Date,String>>, но хотел бы преобразовать его в List<MyCustomClass>, подобный:

public class MyCustomClass() { 
    Date date;
    Long value;
}

Я не могу найти способ инструктировать GSON для сопоставления ключа/значения карты JSON с полями даты и значения в моем пользовательском классе. Есть ли способ сделать это, или список карт - единственный маршрут?

Ответ 1

Вам нужно написать собственный десериализатор. Вам также необходимо использовать формат часового пояса, который SimpleDateFormat может фактически анализировать. Ни z, ни z не будет соответствовать -07:00, что является странным сочетанием формата часового пояса RFC 822 (-0700) или "общего часового пояса" (Mountain Standard Time или MST или GMT-07:00), В качестве альтернативы вы можете придерживаться того же формата часового пояса, и использовать JodaTime DateTimeFormat.

MyCustomClass.java

public class MyCustomClass
{
    Date date;
    Long value;

    public MyCustomClass (Date date, Long value)
    {
        this.date = date;
        this.value = value;
    }

    @Override
    public String toString()
    {
        return "{date: " + date + ", value: " + value + "}";
    }
}

MyCustomDeserializer.java

public class MyCustomDeserializer implements JsonDeserializer<MyCustomClass>
{
    private DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz");

    @Override
    public MyCustomClass deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext ctx) throws JsonParseException
    {
        JsonObject obj = json.getAsJsonObject();
        Entry<String, JsonElement> entry = obj.entrySet().iterator().next();
        if (entry == null) return null;
        Date date;
        try
        {
            date = df.parse(entry.getKey());
        }
        catch (ParseException e)
        {
            e.printStackTrace();
            date = null;
        }
        Long value = entry.getValue().getAsLong();
        return new MyCustomClass(date, value);
    }
}

GsonTest.java

public class GsonTest
{
    public static void main(String[] args)
    {
        // Note the time zone format tweak (removed the ':')
        String json = "[{\"2011-04-30T00:00:00-0700\":100}, {\"2011-04-29T00:00:00-0700\":200}]";

        Gson gson =
            new GsonBuilder()
            .registerTypeAdapter(MyCustomClass.class, new MyCustomDeserializer())
            .create();
        Type collectionType = new TypeToken<Collection<MyCustomClass>>(){}.getType();
        Collection<MyCustomClass> myCustomClasses = gson.fromJson(json, collectionType);
        System.out.println(myCustomClasses);
    }
}

Весь приведенный выше код на Github, не стесняйтесь клонировать (хотя вы также получите код для ответов на другие вопросы).

Ответ 2

Очень похоже на Маттса, но используя Джоду:

import java.lang.reflect.Type;

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;

public class DateTimeSerializer implements JsonDeserializer<DateTime> {

    private DateTimeFormatter parser = DateTimeFormat.forPattern("YYYY-MM-dd'T'HH:mm:ss.SS'Z'").withZoneUTC();

    @Override
    public DateTime deserialize(JsonElement json, Type typeOfT,
            JsonDeserializationContext ctx) throws JsonParseException {
        String dateTimeString = ctx.eserialize(json, String.class);
        return parser.parseDateTime(dateTimeString);
    }

}