Сбой десериализации значения DateTimeOffset в Json.NET для DateTimeOffset.MinValue без часового пояса - PullRequest
0 голосов
/ 31 мая 2018

В моем проекте ASP.NET Core Web-API я получаю HTTP-запрос POST к одному из моих контроллеров API.

При оценке полезной нагрузки JSON и десериализации ее содержимого Json.NET натыкается назначение DateTime, равное 0001-01-01T00:00:00, и не может преобразовать его в свойство DateTimeOffset.

Я заметил, что значение, вероятно, должно представлять значение DateTimeOffset.MinValue, но его отсутствие часового пояса, кажется, отключает десериализаторвверх.Я могу только предположить, что DateTimeOffset.Parse пытается преобразовать его в текущий часовой пояс хостов, что приводит к недостаточному значению DateTimeOffset.MinValue.

Свойство довольно простое:

[JsonProperty("revisedDate", NullValueHandling = NullValueHandling.Ignore)]
public DateTimeOffset? RevisedDate { get; set; }

И вот ответ, отправленный клиенту:

{
    "resource.revisedDate": [
        "Could not convert string to DateTimeOffset: 0001-01-01T00:00:00. Path 'resource.revisedDate', line 20, position 44."
    ]
}

Я использую Newtonsoft.Json v11.0.2 и в настоящее время нахожусь в UTC + 2 (Германия).Трассировка исключения и сообщение об ошибке находятся здесь: https://pastebin.com/gX9R9wq0.

Я не могу исправить вызывающий код, поэтому я должен исправить его на своей стороне линии.

Но вопрос: Как?

Ответы [ 2 ]

0 голосов
/ 31 мая 2018

Проблема кажется воспроизводимой только тогда, когда часовой пояс машины TimeZoneInfo.Local имеет положительное смещение от UTC, например, (UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna.Мне не удалось воспроизвести его в часовых поясах с неположительным смещением, таким как UTC-05:00 или само UTC.

В частности, в JsonReader.ReadDateTimeOffsetString() вызовустанавливается на DateTimeOffset.TryParse с использованием DateTimeStyles.RoundtripKind:

if (DateTimeOffset.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt))
{
    SetToken(JsonToken.Date, dt, false);
    return dt;
}

Это, очевидно, приводит к ошибке недостаточного значения в часовых поясах с положительным смещением UTC.Если в отладчике я анализирую с использованием DateTimeStyles.AssumeUniversal, проблема устраняется.

Возможно, вы захотите сообщить о проблеме об этом в Newtonsoft.Тот факт, что десериализация определенной строки DateTimeOffset завершается неудачей только в том случае, если часовой пояс компьютера имеет определенные значения, кажется неправильным.

Обходной путь должен использовать IsoDateTimeConverterчтобы десериализовать ваши DateTimeOffset свойства с IsoDateTimeConverter.DateTimeStyles, установленным на DateTimeStyles.AssumeUniversal.Кроме того, необходимо отключить автоматическое распознавание DateTime, встроенное в JsonReader, установив JsonReader.DateParseHandling = DateParseHandling.None, что необходимо сделать за до , когда считыватель начнет анализировать значение для вашегоDateTimeOffset properties.

Сначала определите следующее JsonConverter:

public class FixedIsoDateTimeOffsetConverter : IsoDateTimeConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(DateTimeOffset) || objectType == typeof(DateTimeOffset?);
    }

    public FixedIsoDateTimeOffsetConverter() : base() 
    {
        this.DateTimeStyles = DateTimeStyles.AssumeUniversal;
    }
}

Теперь, если вы можете изменить JsonSerializerSettings для вашего контроллера, используйте следующие настройки:

var settings = new JsonSerializerSettings
{
    DateParseHandling = DateParseHandling.None,
    Converters = { new FixedIsoDateTimeOffsetConverter() },
};

Если вы не можете легко изменить JsonSerializerSettings вашего контроллера, вам нужно получить DateParseHandlingConverter с этого ответа до Как предотвратить использование свойства одного объектапреобразуется в DateTime, когда это строка , и применяет его, а также FixedIsoDateTimeOffsetConverter к вашей модели следующим образом:

[JsonConverter(typeof(DateParseHandlingConverter), DateParseHandling.None)]
public class RootObject
{
    [JsonProperty("revisedDate", NullValueHandling = NullValueHandling.Ignore)]
    [JsonConverter(typeof(FixedIsoDateTimeOffsetConverter))]
    public DateTimeOffset? RevisedDate { get; set; }
}

DateParseHandlingConverter должно применяться скорее к самой моделичем свойство RevisedDate, поскольку JsonReader уже распознает 0001-01-01T00:00:00 как DateTime до того, как будет сделан вызов FixedIsoDateTimeOffsetConverter.ReadJson().

Обновление

В комментариях , @ RenéSchindhelm пишет, Я создалпроблема, чтобы Newtonsoft знал .Это Сбой десериализации значения DateTimeOffset в зависимости от часового пояса системы # 1731 .

0 голосов
/ 31 мая 2018

Проверьте свою версию Json.NET, а затем свое входное значение и форматирование.Я пытаюсь следующий пример, и он работает нормально для меня:

void Main()
{
    var json = @"{""offset"":""0001-01-01T00:00:00""}";
    var ds = Newtonsoft.Json.JsonConvert.DeserializeObject<TestDS>(json);
    Console.WriteLine(ds);
}
public class TestDS {
    [Newtonsoft.Json.JsonProperty("offset", NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
    public DateTimeOffset? DSOffset { get; set; }
}

Вот вывод:

DSOffset 1/1/0001 12:00:00 AM-06: 00

...