用Java序列化日期

时间:2020-03-06 14:32:15  来源:igfitidea点击:

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

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

第二部分比较棘手:一旦我决定如何传递日期,就可以明显地声明日期瞬态,并使一些包装类将日期传递为String或者其他形式,但是如何尽可能透明地将相同的解决方案应用于包括Date的多个类?

(我有一种预感,DynamicProxy可能是一个解决方案,但是阅读Sun网站上的文档并不是很有帮助,因此,如果确实是朝着这个方向发展,那么请我们进行一些澄清)

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

解决方案

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

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

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

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

我们不需要默认构造函数(空)即可序列化/反序列化日期(java.sql.Date或者java.util.Date)。在反序列化期间,不会调用构造函数,而是将对象的属性直接设置为序列化数据中的值,并且可以按原样使用该对象,因为它已反序列化。

如先前建议的那样,序列化Date.getTime()返回的long即可。但是,我们应该注意,如果服务器位于客户端以外的其他时区,则在另一端重构的日期将有所不同。如果要重建完全相同的日期对象,则还需要发送时区(TimeZone.getID())并使用它在另一侧重建日期。

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

这是序列化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();

我已经研究了java.sql.Date的实现,并且看到java.sql.Date可序列化为java.util.Date的扩展。

Date类的API笨拙。更好的实现是Joda-Time。

Joda-Time还允许我们将日期转换为ISO 8601标准格式(yyyy-mm-ddTHH:MM:SS.SSS)。在将日期从服务器移到其客户端时使用此标准的优点是可以以可读格式包含完整日期。例如,当使用JAXB时,日期的XML表示形式也是该ISO标准。 (请参阅XMLGregorianCalendar类)

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

对于第二部分,我不确定为什么我们需要代理类?还是为什么我们必须扩展date类来支持这一点。例如。Web服务是否不知道某个字段是日期,并且从日期到字符串的转换并返回本身?我需要更多信息。

java.sql.Date已经实现了Serializable,因此无需实现它:-)

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

嗯...无法想到任何序列化的对象实例(通过默认的java机制序列化)应该将自身反序列化为另一个类的实例的任何原因,因为类信息应该是序列化数据的固有部分。

因此,这可能是(反序列化)框架的问题,或者该框架在"发送端"(日历,java.util.Date等,因此也包含java.sql.Date)上接受了任何"日期型"对象扩展java.util.Date),将其"序列化"为某种通用日期格式的字符串(这样会丢失类型信息),然后将其"反序列化"回接收端的Calendar对象。

所以我认为进入java.sql.Date的最简单方法是

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

需要java.sql.Date的地方,但是从"反序列化"中获取GregorianCalendar。

关于java.sql.Date的一个警告是最近它引起我的注意的是它不存储时间部分(小时,分钟,秒等),而仅存储日期部分。如果需要完整的时间戳,则必须使用java.util.Date或者java.sql.Timestamp