为什么我的 DateTime 反序列化会截断 DateTimes minute/second/millisecond?

Why is my DateTime deserializer is truncating DateTime's minute/second/millisecond?

我有一个 class 那个 deserializes 一个 JSON 元素。

public class DateTimeConverter implements JsonSerializer<DateTime>, JsonDeserializer<DateTime>
{
    private static final DateTimeFormatter DATE_FORMAT = ISODateTimeFormat.dateHourMinuteSecondMillis();
    @Override
    public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context)
    {
        final DateTimeFormatter fmt = ISODateTimeFormat.dateHourMinuteSecondMillis();
        return new JsonPrimitive(fmt.print(src));
    }


    @Override
    public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
            throws JsonParseException
    {
        final String dateAsString = json.getAsString();
        System.out.println(dateAsString);
        if (json.isJsonNull() || dateAsString.length()==0)
        {
            return null;
        }
        else
        {
            return DATE_FORMAT.parseDateTime(json.getAsString());
        }
    }
}

然而,我的 Deserialize 方法当我输入时:

2015-07-29T11:00:00.000Z

我收到:

2015-07-29T11

来自 System.out.println(dateAsString); 为什么它会截断我的输入?

我认为我的问题在我的测试中 class:

我构建了一个 DateTime 对象与 Google 的 Gson 一起使用。但是,我认为 DateTimeType 的默认构造函数不支持 minute/second/millisecond。有什么方法可以扩展 DateTimeType 来支持它吗?

这是我的测试class:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

import org.joda.time.DateTime;
import org.junit.Test;

import java.lang.reflect.Type;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
/**
 * Tests {@link DateTimeConverter}.
 */
public class DateTimeConverterTest {
    String testTime = "2015-07-29T11:00:00.001Z";

    @Test
    public void testDateTimeConverter() throws Exception {
        final Gson gson = initCustomGSON();
        Type DateTimeType = new TypeToken<DateTime>() {
        }.getType();

        System.out.println(testTime);
        DateTimeConverter timeConverter = new DateTimeConverter();
        DateTime m = (gson.fromJson(testTime, DateTimeType));

        assertThat("11", is(m.hourOfDay().getAsText()));
    }

    public Gson initCustomGSON() {
        final GsonBuilder builder = new GsonBuilder();
        JodaTimeConverters converter = new JodaTimeConverters();
        converter.registerAll(builder);
        return builder.create();
    }

}

这段代码有一些问题。

  1. 您的第一个问题是 : 是 Json 中的运算符。您正在解释其中包含 : 的未转义 String,因此 Gson 将其解释为 key : value。您的测试字符串需要用引号将整个文本日期括起来,以防止发生这种情况,例如

    String testTime = "\"2015-07-29T11:00:00.001Z\"";
    
  2. 您使用的是 ISODateTimeFormat.dateHourMinuteSecondMillis() in your code. However, the format pattern for this is yyyy-MM-dd'T'HH:mm:ss.SSS, which as you can see does not include a time zone. You want to be using ISODateTimeFormat.dateTime(),其模式是 yyyy-MM-dd'T'HH:mm:ss.SSSZZ,它确实有时区。

    private static final DateTimeFormatter DATE_FORMAT = ISODateTimeFormat.dateTime();
    
  3. 完成这两项更改后,DateTime 对象终于正确创建了...但它将在 您的 本地时间创建时区,而不是 UTC(它会正确调整您所在时区的时间。您可以通过以下方式轻松将其切换回 UTC:

    DateTime m = ((DateTime) (gson.fromJson(testTime, DateTimeType))).withZone(DateTimeZone.UTC);
    

完成这三项更改后,您的测试就会通过。 但是: 我强烈建议不要使用 JsonSerializerJsonDeserializer,它们已被弃用,取而代之的是 TypeAdapter, whose streaming API is significantly more performant:

New applications should prefer TypeAdapter, whose streaming API is more efficient than this interface's tree API.

我知道用户指南提供了如何使用 JsonSerializer / JsonDeserializer API 执行此操作的代码,但这只是因为他们尚未更新它。

它会像这样:

public class DateTimeAdapter extends TypeAdapter<DateTime> {
  private static final DateTimeFormatter FORMAT = ISODateTimeFormat.dateTime();

  public DateTime read(JsonReader reader) throws IOException {
    if (reader.peek() == JsonToken.NULL) {
      reader.nextNull();
      return null;
    }
    String dateString = reader.nextString();
    if(dateString.length() == 0) return null;
    return FORMAT.parseDateTime(dateString);
  }

  public void write(JsonWriter writer, DateTime value) throws IOException {
    if (value == null) {
      writer.nullValue();
      return;
    }
    writer.value(FORMAT.print(value));
  }
}