在Java中序列化日期

我通过Web服务传递一些对象,其中一些包含java.sql.Date。 因为Date没有空构造函数,所以它不希望序列化。

问题的第一部分很简单:在客户端和服务之间传递日期的最佳方法是什么?

第二部分有点棘手:一旦我决定如何传递日期,我显然可以声明日期瞬态并使一些包装类将日期作为字符串或其他方式传递,但如何将相同的解决方案尽可能透明地应用于包含Date的几个类?

(我有一种预感,DynamicProxy可能是一个解决方案,但阅读Sun网站上的文档并不是很有帮助,所以如果它确实是朝这个方向发展的话,那么一些澄清将会受到赞赏)

编辑:我问了错误的问题,对不起(我和同事之间的一些误解实际上是一个问题)。 由于反序列化而出现问题。 因此,一旦我有xml格式的日期,它会尝试将自身反序列化为GregorianCalendar。 问题的其他部分仍然存在:接收某些东西(长时间戳或GregorianCalendar)并将其转换为sql日期的最佳方式是什么,而不为10个不同的类制作10个不同的包装器。 我正在使用NetBeans代码和wsdl生成。

乔达时间

Date类有一个笨重的API。 更好的实施是Joda-Time 。

ISO 8601

Joda-Time还允许您以ISO 8601标准格式转换日期(yyyy-mm-ddTHH:MM:SS.SSS)。 将日期从服务器移动到其客户端时使用此标准有利于以可读格式包含完整日期。 当您使用例如JAXB时 ,日期的XML表示也是此ISO标准。 (参见XMLGregorianCalendar类)

如前所述,序列化由Date.getTime()返回的long将起作用。 但是,您应该注意,如果您的服务器位于客户端以外的其他时区,则您在另一侧重建的日期将会有所不同。 如果您想要重建完全相同的日期对象,您还需要发送您的时区(TimeZone.getID())并使用它来重建另一侧的日期。

为了回答你问题的第一部分,我建议使用iso 8601格式的字符串(这是编码日期的标准)。

对于第二部分,我不确定为什么需要代理类? 或者为什么你必须扩展日期类来支持它。 例如。 你的网络服务不会知道某个字段是一个日期并从日期转换为字符串并返回自身吗? 我需要更多信息。

java.sql.Date扩展了java.util.Date

只需使用getTime()从中获取long值。 这可以序列化,并在另一端从它构造一个新的java.sql.Date(long)或新的java.util.Date(long)。

我已经研究了java.sql.Date的实现,因为我看到java.sql.Date是Serializable作为java.util.Date的扩展。

我最近咬了一下java.sql.Date的一个警告是它不存储时间部分(小时,分钟,秒等)只是日期部分。 如果你想要完整的时间戳,你必须使用java.util.Date或java.sql.Timestamp

我将通过JeroenWyseur扩展正确的答案 。

ISO 8601

ISO 8601标准格式绝对是序列化数据交换的日期时间值的最佳方式。 这种格式对于跨文化的人们来说是明确的,直观的,并且在世界各地越来越普遍。 易于阅读的人类和机器。

2015-01-16T20:15:43+02:00 2015-01-16T18:15:43Z 

第一个示例的偏移量比UTC早两个小时。 第二个示例显示了Z (“Zulu”)的常用用途,表示UTC,简称为+00:00

Joda-Time和java.time

与Java捆绑在一起的java.util.Date和.Calendar类是众所周知的麻烦,令人困惑和有缺陷的。 避免他们。 而是使用:

  • Joda-Time图书馆
  • java.time包,内置于Java 8中,受JSR 310定义的Joda-Time启发。

默认情况下,两个库都使用ISO 8601进行解析和生成日期时间值的字符串表示。

请注意,java.time通过附加时区的正确名称来扩展ISO 8601格式,例如2007-12-03T10:15:30+01:00[Europe/Paris]

通过这些库的大量讨论和示例代码,搜索StackOverflow.com数百个问题和解答。

避免Count-From-Epoch

其他一些答案建议使用一个数字,一个来自时代的计数。 这种方法不实用。 这不是不证自明的。 它不是人类可读的,使调试麻烦和令人沮丧。

它是哪个数字,Unix中常用的整秒,java.util.Date和Joda-Time中使用的毫秒数,Postgres等数据库中常用的微秒数,或java.time包中使用的纳秒数 ?

哪几个时代 ,1970年的第一个时刻,用于.Net&Go的第一个时刻,“1900年1月0日”用于数百万(数十亿?)的Excel和Lotus电子表格,或2001年1月1日使用的cocoa ?

时间跟踪的不同分辨率的图表

有关更多讨论,请参阅我对类似问题的回答 。

您不需要默认构造函数(空)来序列化/反序列化日期(java.sql.Date或java.util.Date)。 在反序列化期间,不会调用构造函数,但是对象的属性直接设置为来自序列化数据的值,并且您可以在反序列化后使用该对象。

您可以使用编码器和解码来序列化和反序列化您的对象。

这是一个序列化SWT Rectangle类的示例:

 XMLEncoder encoder = new XMLEncoder(new FileOutputStream(file)); encoder.setPersistenceDelegate( Rectangle.class, new DefaultPersistenceDelegate(new String[]{"x", "y", "width", "height"})); encoder.writeObject(groups); encoder.close(); 

首先,如果您正在使用Web服务,则意味着您要序列化为XML而不是常规Java序列化(但是一些其他库用于编组和解组)。 所以问题是缺少一些信息。

其次,如果您可以控制InputStream和OutputStream,请尝试扩展ObjectOutputStream和ObjectInputStream并重写replaceObject()resolveObject() ,然后您可以实现java.sql.Date的序列化。

java.sql.Date已经实现了Serializable,所以不需要实现它:-)

就您的主要问题而言,我非常喜欢JAXB,因为我几乎可以将任何XML转换为对象,因此您可能值得花时间去研究它。

嗯…想不出任何理由为什么任何序列化的对象 – 实例(通过默认的java机制序列化)应该将自身反序列化为另一个类的实例,因为类信息应该是序列化数据的固有部分。

所以这是你的(反)序列化框架的问题,或者框架接受“发送端”上的任何“类似日期”的对象(Calendar,java.util.Date等 – 因此java.sql.Date也是如此它扩展了java.util.Date),以一些常见的日期格式将其“序列化”为String(因此类型信息丢失),并将其“反序列化”回接收端的Calendar对象。

所以我认为获得java.sql.Date的最简单方法是做一个

 java.sql.Date date = new java.sql.Date(calendar.getTimeInMillis); 

你需要一个java.sql.Date,但从“反序列化”中获取GregorianCalendar。