Ошибка десериализации .Net Core 3.0 TimeSpan - PullRequest
0 голосов
/ 08 октября 2019

Я использую .Net Core 3.0 и у меня есть следующая строка, которую нужно десериализовать с помощью Newtonsoft.Json:

{
    "userId": null,
    "accessToken": null,
    "refreshToken": null,
    "sessionId": null,
    "cookieExpireTimeSpan": {
        "ticks": 0,
        "days": 0,
        "hours": 0,
        "milliseconds": 0,
        "minutes": 0,
        "seconds": 0,
        "totalDays": 0,
        "totalHours": 0,
        "totalMilliseconds": 0,
        "totalMinutes": 0,
        "totalSeconds": 0
    },
    "claims": null,
    "success": false,
    "errors": [
        {
            "code": "Forbidden",
            "description": "Invalid username unknown!"
        }
    ]
}

и возникла следующая ошибка:

   Newtonsoft.Json.JsonSerializationException : Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.TimeSpan' because the type requires a JSON primitive value (e.g. string, number, boolean, null) to deserialize correctly.
To fix this error either change the JSON to a JSON primitive value (e.g. string, number, boolean, null) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path 'cookieExpireTimeSpan.ticks', line 1, position 103.

Строка ошибки фактически возникает при чтении содержимого HttpResponseMessage:

var httpResponse = await _client.PostAsync("/api/auth/login", new StringContent(JsonConvert.SerializeObject(new API.Models.Request.LoginRequest()), Encoding.UTF8, "application/json"));
var stringResponse = await httpResponse.Content.ReadAsStringAsync();

Метод контроллера сервера возвращает:

return new JsonResult(result) { StatusCode = whatever; };

1 Ответ

3 голосов
/ 08 октября 2019

Служба REST API не должна создавать такую ​​строку JSON. Держу пари, что предыдущие версии возвращали 00:0:00 вместо всех свойств объекта TimeSpan.

Причина этого в том, что .NET Core 3.0 заменил JSON.NET новым, встроенный JSON-сериализатор, System.Text.Json . Этот сериализатор не поддерживает TimeSpan . Новый сериализатор работает быстрее, в большинстве случаев не распределяется, но не охватывает все случаев, которые делал JSON.NET.

В любом случае, есть нет Стандартный способ представления дат или периодов в формате JSON. Даже формат ISO8601 является соглашением, а не частью самого стандарта. JSON.NET использует читаемый формат (23:00:00), но формат ISO8601 будет выглядеть как P23DT23H (23 дня, 23 часа) или P4Y (4 года).

Одним из решений является возврат к JSON.NET. Шаги описаны в документации:

services.AddMvc()
    .AddNewtonsoftJson();

Другой вариант - использовать пользовательский конвертер для этого типа, например:

public class TimeSpanToStringConverter : JsonConverter<TimeSpan>
{
    public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var value=reader.GetString();
        return TimeSpan.Parse(value);
    }

    public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString());
    }
}

и зарегистрировать егов Startup.ConfigureServices с AddJsonOptions, например:

services.AddControllers()                    
        .AddJsonOptions(options=>
            options.JsonSerializerOptions.Converters.Add(new TimeSpanToStringConverter()));

...