Исключения OData4j - "Нечетное количество символов" и "неправильная часть valueString keyString"
РЕДАКТИРОВАТЬ:
Решение состояло в том, чтобы создать представление, отражающее данную таблицу и преобразовавшее дату в varchar, а затем преобразовать ее обратно в дату с соответствующим сопоставлением.
Конец редактирования
Может кто-нибудь сказать мне, почему OData4j прекрасно считывает значения даты и времени с одного из моих серверов Службы данных WCF, но сталкивается с недопустимым исключением аргумента (неверное значение valueString как часть keyString) при чтении точно такого же типа даты и времени с тем же форматом из другой Службы данных WCF?
java.lang.IllegalArgumentException: неверное значение String [datetime'2012-01-24T14%3A57%3A22.243'] как часть keyString
Другая проблема заключается в том, что, когда я запрашиваю ответ JSON от службы, из которой у OData4j не было проблем с чтением типов даты и времени, я получаю еще одно недопустимое исключение аргумента и сообщение об ошибке: "Нечетное количество символов".
java.lang.IllegalArgumentException: org.odata4j.repack.org.apache.commons.codec.DecoderException: нечетное количество символов.
Поскольку службы данных WCF не могут иметь несколько источников, я создал 2 проекта, каждый из которых имеет собственный источник модели данных Entity (из существующей базы данных). И, как я упоминал выше, я получаю эти досадные ошибки.
Заключить...
Пример 1: неверное значение valueString как часть keyString - при чтении даты и времени. Также случается с FormatType.JSON
,
ODataConsumer customerInfoServices = ODataConsumer
.newBuilder("http://10.0.2.2:41664/CustomerInfoWCFDataServices.svc/")
.setFormatType(FormatType.ATOM)
.build();
customer = customerInfoServices
.getEntities("Customers")
.select("name, id")
.filter("id eq " + 5)
.execute()
.firstOrNull();
Пример 2: Нечетное количество символов. Только случается с FormatType.JSON
и нет проблем с чтением даты и времени.
ODataConsumer businessServices = ODataConsumer
.newBuilder("http://10.0.2.2:35932/BusinessWCFDataServices.svc/")
.setFormatType(FormatType.JSON)
.build();
Enumerable<?> ordrer = businessServices
.getEntities("Orders")
.filter("custId eq " + customer.getProperty("id").getValue())
.execute();
Я хочу получать ответы в формате JSON (ATOM все еще слишком загружен для Android) и без проблем с чтением свойств datetime.
Нет тела, способного помочь мне?
Я измотал свои пальцы, пытаясь найти решение в Google, без какой-либо удачи.
Параметры сортировки в базе данных без проблем даты и времени - "Danish_Norwegian_CI_AS", а в базе данных с ошибками чтения - "SQL_Danish_Pref_CP1_CI_AS". Я не знаю, имеет ли это какое-то значение, но у меня есть подозрение, что это как-то связано с этим.
2 ответа
Решение состояло в том, чтобы создать представление, отражающее данную таблицу и преобразовавшее дату в varchar, а затем преобразовать ее обратно в дату с соответствующим сопоставлением.:-)
Я столкнулся с этой проблемой сегодня, и после трех часов бегания по Google, глядя на код, мне удалось выяснить, что происходит. Вот моя установка / ситуация и что я нашел:
Настройка
- (Служба OData) Microsoft IIS 8.0 в Windows Server 2012 с использованием пула приложений по умолчанию.
- (OData Producer) Microsoft WCF среднего уровня с использованием Entity Framework и Web Data Services.
- (Потребитель OData) Android-клиент, использующий OData4J v0.8 SNAPSHOT.
Эта проблема
На моем среднем уровне (OData Producer) я использую Entity Framework 5.0 для определения простой таблицы с Edm.DateTime
колонка. мой MessageTable.edmx
Файл генерирует простую таблицу:
CREATE TABLE [dbo].[MessageTable] (
[Id] int IDENTITY(1,1) NOT NULL,
[Date1] datetime NULL
);
В службе данных WCF моего среднего уровня (OData Producer) я перехватываю OData POST из моего клиентского приложения Android. Я вручную установил столбец Date1 на среднем уровне, используя некоторый код C#:
private static void ProcessMessage(ODataMessage message)
{
.
.
.
conn.Open();
cmd.ExecuteNonQuery();
int returnCode = (int)cmd.Parameters["@result"].Value;
if (returnCode == 0)
{
message.Processed = true;
message.Date1 = DateTime.Now;
}
.
.
.
}
Обратите внимание, что я использовал статическое свойство C# DateTime.Now. Документация MSDN гласит, что DateTime.Now
:
Получает объект DateTime, для которого установлены текущие дата и время на этом компьютере, выраженные в виде местного времени.
Главное, что нужно понять, это то, что местное время означает, что структура даты и времени в C# теперь включает информацию о часовом поясе.
Итак, после завершения кода среднего уровня служба WCF вставляет OData POST в мою таблицу. Microsoft затем отправляет [Date1]
Вернуться к моему клиенту Android (Потребитель OData). Похоже, что Microsoft кодирует дату как OData DateTimeOffset, потому что она включает информацию о местном часовом поясе. То есть как 2013-08-26T17:30:00.0000000-7:00
Код
В OData4j есть пакет org.odata.internal
который обрабатывает строки даты и времени OData. В версии 0.6 я нашел следующий комментарий в строках 40-44 о DATETIME_PATTERN
Шаблон регулярного выражения, используемый для анализа строк даты и времени:
40 // Since not everybody seems to adhere to the spec, we are trying to be
41 // tolerant against different formats
42 // spec says:
43 // Edm.DateTime: yyyy-mm-ddThh:mm[:ss[.fffffff]]
44 // Edm.DateTimeOffset: yyyy-mm-ddThh:mm[:ss[.fffffff]](('+'|'-')hh':'mm)|'Z'
45 private static final Pattern DATETIME_PATTERN =
46 Pattern.compile("(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2})(:\\d{2})?(\\.\\d{1,7})?((?:(?:\\+|\\-)\\d{2}:\\d{2})|Z)?");
47
48
В OData4j v0.7 DATETIME_PATTERN
стал DATETIME_XML_PATTERN
:
40
41 private static final Pattern DATETIME_XML_PATTERN = Pattern.compile("" +
42 "^" +
43 "(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2})" + // group 1 (datetime)
44 "(:\\d{2})?" + // group 2 (seconds)
45 "(\\.\\d{1,7})?" + // group 3 (nanoseconds)
46 "(Z)?" + // group 4 (tz, ignored - handles bad services)
47 "$");
48
49 private static final Pattern DATETIMEOFFSET_XML_PATTERN = Pattern.compile("" +
50 "^" +
51 "(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2})" + // group 1 (datetime)
52 "(\\.\\d{1,7})?" + // group 2 (nanoSeconds)
53 "(((\\+|-)\\d{2}:\\d{2})|(Z))" + // group 3 (offset) / group 6 (utc)
54 "$");
Я думаю, что комментарий к строке 46 объясняет все:
... // группа 4 (tz, игнорируется - обрабатывает плохие сервисы)
Я интерпретирую это как: "Любая информация о часовом поясе будет игнорироваться - это обрабатывает плохие сервисы (например, Microsoft), которые отправляют информацию о часовом поясе".
Мне кажется, что авторы OData4j решили придерживаться своего оружия и принимают только правильные Edm.DateTime
формат строки
Решение
Исправить это очень просто, если у вас есть доступ к коду OData Producer:
private static void ProcessMessage(ODataMessage message)
{
.
.
.
conn.Open();
cmd.ExecuteNonQuery();
int returnCode = (int)cmd.Parameters["@result"].Value;
if (returnCode == 0)
{
message.Processed = true;
message.Date1 = DateTime.UtcNow;
}
.
.
.
}
Вместо этого используйте свойство DateTime.UtcNow. Документация MSDN гласит:
Получает объект DateTime, для которого на текущем компьютере установлены текущие дата и время, выраженные как всемирное координированное время (UTC).
Если у вас нет доступа к производителю OData, единственное решение, которое я вижу, это изменить код OData4j. Изменить шаблон регулярного выражения для DATETIME_XML_PATTERN
:
41 private static final Pattern DATETIME_XML_PATTERN = Pattern.compile("" +
42 "^" +
43 "(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2})" + // group 1 (datetime)
44 "(:\\d{2})?" + // group 2 (seconds)
45 "(\\.\\d{1,7})?" + // group 3 (nanoseconds)
46 "((?:(?:\\+|\\-)\\d{2}:\\d{2})|Z)?" +
47 "$");
Заключение
Я думаю, что это на самом деле ошибка со стороны Microsoft. Я уверен, что у них есть некоторые обоснованные причины для отправки Edm.DateTimeOffset
но мой MessageTable.edmx
файл указывает [Date1]
как Edm.DateTime
Поэтому я считаю, что код Microsoft должен либо выполнить для меня преобразование в UTC, либо выдать исключение. Исключение предупредило бы меня о том, что я использовал неправильную структуру DateTime, и помогло бы мне увидеть решение DateTime.UtcNow намного быстрее.