Сериализация даты в Java

Я передаю некоторые объекты через веб-службу, а некоторые из них содержат java.sql.Date. Поскольку у Date нет пустого конструктора, он не хочет сериализоваться.

Первая часть вопроса проста: как лучше всего провести свидание между клиентом и службой?

Вторая часть немного сложнее: как только я решу, как передавать даты, я, очевидно, могу объявить временную дату и сделать некоторый класс-оболочку для передачи дат как String или что-то еще, но как применить то же решение как можно более прозрачно к нескольким классам, которые включают Date ?

(У меня есть подозрение, что DynamicProxy может быть решением, но чтение документации на сайте Sun не очень помогло, поэтому, если это действительно что-то в этом направлении, некоторые пояснения были бы признательны)

Обновлено: я задал неправильный вопрос, извините (какое-то недоразумение между мной и коллегой, что на самом деле является проблемой). Проблема возникает из-за десериализации. Итак, как только у меня есть дата в формате xml, он пытается десериализовать себя как GregorianCalendar. Остается еще одна часть вопроса: как лучше всего получить что-либо (длинную временную метку или GregorianCalendar) и преобразовать это в дату sql, не создавая 10 разных оболочек для 10 разных классов. Я использую NetBeans для создания кода и wsdl.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
8
0
23 632
12

Ответы 12

java.sql.Date расширяет java.util.Date

Просто используйте getTime (), чтобы получить от него длинное значение. Это может быть сериализовано, и на другом конце из него может быть создан новый java.sql.Date (long) или новый java.util.Date (long).

Как было сказано выше, при этом теряется часовой пояс, содержащийся в Date.

Coderer 19.08.2011 21:06

Во-первых, если вы используете веб-службы, это означает, что вы сериализуете в XML, а не в обычную сериализацию Java (а в какую-то другую библиотеку для маршалинга и демаршалинга). Так что в вопросе не хватает информации.

Во-вторых, если у вас есть контроль над InputStream и OutputStream, попробуйте расширить ObjectOutputStream и ObjectInputStream и переопределить replaceObject () и resolveObject (), а затем вы можете реализовать сериализацию для java.sql.Date.

Вам не нужен конструктор по умолчанию (пустой) для сериализации / десериализации даты (либо java.sql.Date, либо java.util.Date). Во время десериализации конструктор не вызывается, но атрибуты объекта устанавливаются непосредственно на значения из сериализованных данных, и вы можете использовать объект как есть, поскольку он десериализован.

Сериализация long, возвращаемого Date.getTime (), как было предложено ранее, будет работать. Однако вы должны отметить, что если ваш сервер находится в другом часовом поясе, чем клиент, дата, которую вы реконструируете на другой стороне, будет другой. Если вы хотите восстановить точно такой же объект даты, вам также необходимо отправить свой часовой пояс (TimeZone.getID ()) и использовать его для восстановления даты на другой стороне.

если вы сериализуете 2 часа дня в Париже, вы получите 8 утра, если десериализуете тот же поток на машине, настроенной для часового пояса Нью-Йорка. Если вы хотите запланировать встречу, это нормально, потому что вы хотите, чтобы все присутствовали одновременно. Но если вам нужно отобразить информацию о контракте (например, этот параметр истекает в 14:00 в Париже), вам необходимо отправить часовой пояс, чтобы компьютер Нью-Йорка определил, что в Париже 14:00, а не 8:00, и, кстати,. Бизнес-модели существуют для обеих ситуаций, отправка по часовому поясу всегда безопасна, за исключением случаев, когда у вас есть строгие ограничения полосы пропускания.

entzik 24.01.2012 17:05

Вы можете использовать кодировщик и декодировать для сериализации и десериализации ваших объектов.

Вот пример, который сериализует класс 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. Лучшая реализация - Джода-Тайм.

ISO 8601

Joda-Time также позволяет преобразовывать дату в стандартный формат ISO 8601 (гггг-мм-ддTHH: MM: SS.SSS). Использование этого стандарта при переносе дат с сервера на его клиент имеет то преимущество, что включает полную дату в удобочитаемом формате. Когда вы используете, например, JAXB, XML-представление даты также является этим стандартом ISO. (см. класс XMLGregorianCalendar)

Также обратите внимание, что предстоящий JSR 310 Date & Time API для Java 7 будет основан на ISO8601 и концепциях от Joda Time. Так что это хорошее направление на будущее.

Alex Miller 22.09.2008 17:44

Спасибо! Сам не знал. Наконец, лучшая реализация Date.

Jeroen Wyseur 23.09.2008 14:32

Хорошая статья о новом API: today.java.net/pub/a/today/2008/09/18/…

Jeroen Wyseur 25.09.2008 11:40

Чтобы ответить на первую часть вашего вопроса, я бы предложил строку в формате iso 8601 (это стандарт кодирования дат).

Что касается второй части, я не уверен, зачем вам нужен прокси-класс? Или почему вам нужно было бы расширить класс даты для поддержки этого. например. разве ваша веб-служба не узнает, что определенное поле является датой, и не выполнит ли преобразование даты в строку и обратно? Мне нужно немного больше информации.

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 обратно из "десериализации".

одно предостережение с 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.

java.time

Классы java.util.Date и .Calendar, связанные с Java, общеизвестно хлопотны, сбивают с толку и ошибочны. Избежать их. Вместо этого используйте:

  • Пакет java.time, встроенный в Java 8, вдохновленный Joda-Time, определенный JSR 310.

Пакет java.time заменяет своего предшественника, библиотеку Джода-Тайм.

По умолчанию обе библиотеки используют ISO 8601 как для синтаксического анализа, так и для генерации строковых представлений значений даты и времени.

Обратите внимание, что java.time расширяет формат ISO 8601, добавляя собственное имя часового пояса, например 2007-12-03T10:15:30+01:00[Europe/Paris].

Найдите на StackOverflow.com сотни вопросов и ответов с подробным обсуждением и примерами кода.

Избегайте отсчета эпохи

В некоторых других ответах рекомендуется использовать число, количество от эпоха. Такой подход непрактичен. Это не самоочевидно. Он не читается человеком, поэтому отладка затруднительна и утомительна.

Какое это число: целые секунды, обычно используемые в Unix, миллисекунды, используемые в java.util.Date и Joda-Time, микросекунды, обычно используемые в базах данных, таких как Postgres, или наносекунды, используемые в пакет java.time?

Какой из пара десятков эпох, первый момент 1970 года, используемый в Unix, год 1, используемый в .Net & Go, «0 января 1900 года», используемый в миллионах (миллиардах?) Таблиц Excel и Lotus, или 1 января 2001 года, используемый Какао?

diagram of different resolution of time tracking

См. мой ответ по аналогичному вопросу для более подробного обсуждения.

LocalDate

I'm passing around some objects through web service and some of them contain java.sql.Date

Замена ужасного класса java.sql.Date - java.time.LocalDate.

Лучше всего полностью избегать устаревшего класса, но вы можете конвертировать туда и обратно, вызывая новые методы, добавленные к старому классу: myJavaSqlDate.toLocalDate()

Сериализация LocalDate

Класс LocalDate реализует Serializable. Таким образом, у вас не должно возникнуть проблем с его автоматической сериализацией, как маршалингом, так и демаршалингом.


О java.time

Фреймворк java.time встроен в Java 8 и новее. Эти классы заменяют неудобные старые классы даты и времени наследие, такие как java.util.Date, Calendar, & SimpleDateFormat.

Чтобы узнать больше, см. Учебник Oracle. И поищите в Stack Overflow множество примеров и объяснений. Спецификация - JSR 310.

Проект Джода-Тайм, теперь в Режим технического обслуживания, советует перейти на классы java.time.

Вы можете обмениваться объектами java.time напрямую с вашей базой данных. Используйте Драйвер JDBC, совместимый с JDBC 4.2 или новее. Нет необходимости в строках, нет необходимости в классах java.sql.*.

Где взять классы java.time?

  • Java SE 8, Java SE 9, Java SE 10, Java SE 11 и более поздние версии - часть стандартного Java API с объединенной реализацией.
    • В Java 9 добавлены некоторые незначительные функции и исправления.
  • Java SE 6 и Java SE 7
    • Большая часть функциональности java.time перенесена на Java 6 и 7 в ThreeTen-Backport.
  • Android
    • Более поздние версии Android связывают реализации классов java.time.
    • Для более ранних версий Android (<26) проект ThreeTenABP адаптирует ThreeTen-Backport (упомянутый выше). См. Как использовать ThreeTenABP….

Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является испытательной площадкой для возможных будущих дополнений к java.time. Здесь вы можете найти несколько полезных классов, таких как Interval, YearWeek, YearQuarter и более.

Другие вопросы по теме