为什么我的DateTime反序列化器会截断DateTime的分钟/秒/毫秒?

时间:2015-07-29 19:59:39

标签: java json datetime gson jodatime

我有一个deserializesJSON元素的课程。

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

为什么要截断我的输入?

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

我构建了一个DateTime对象,用于Google的Gson。但是,我认为DateTimeType的默认构造函数不支持分/秒/毫秒。有没有办法可以扩展DateTimeType以支持它?

这是我的测试类:

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 个答案:

答案 0 :(得分:3)

此代码存在一些问题。

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

    String testTime = "\"2015-07-29T11:00:00.001Z\"";
    
  2. 您在代码中使用了ISODateTimeFormat.dateHourMinuteSecondMillis()。但是,格式模式为yyyy-MM-dd'T'HH:mm:ss.SSS,您可以看到它不包含时区。您希望使用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);
    
  4. 完成这三项更改后,您的测试即可通过。 但是:我强烈建议您不要使用JsonSerializerJsonDeserializer,而是弃用了TypeAdapter,其流API为significantly more performant:

      

    新应用程序应该更喜欢TypeAdapter,其流API比此接口的树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));
      }
    }