Исключения 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, глядя на код, мне удалось выяснить, что происходит. Вот моя установка / ситуация и что я нашел:

Настройка

  1. (Служба OData) Microsoft IIS 8.0 в Windows Server 2012 с использованием пула приложений по умолчанию.
  2. (OData Producer) Microsoft WCF среднего уровня с использованием Entity Framework и Web Data Services.
  3. (Потребитель 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 намного быстрее.

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