.Net Core 3.0 TimeSpan deserialization error - Fixed in .Net 5.0

后端 未结 3 2173
抹茶落季
抹茶落季 2021-02-13 23:27

I am using .Net Core 3.0 and have the following string which I need to deserialize with Newtonsoft.Json:

{
    \"userId\": null,
    \"accessToken\": null,
    \         


        
相关标签:
3条回答
  • 2021-02-13 23:38

    The REST API service shouldn't produce such a JSON string. I'd bet that previous versions returned 00:0:00 instead of all the properties of a TimeSpan object.

    The reason for this is that .NET Core 3.0 replaced JSON.NET with a new, bult-in JSON serializer, System.Text.Json. This serializer doesn't support TimeSpan. The new serializer is faster, doesn't allocate in most cases, but doesn't cover all the cases JSON.NET did.

    In any case, there's no standard way to represent dates or periods in JSON. Even the ISO8601 format is a convention, not part of the standard itself. JSON.NET uses a readable format (23:00:00), but ISO8601's duration format would look like P23DT23H (23 days, 23 hours) or P4Y (4 years).

    One solution is to go back to JSON.NET. The steps are described in the docs:

    • Add a package reference to Microsoft.AspNetCore.Mvc.NewtonsoftJson.

    • Update Startup.ConfigureServices to call AddNewtonsoftJson.

    services.AddMvc()
        .AddNewtonsoftJson();
    

    Another option is to use a custom converter for that type, eg :

    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());
        }
    }
    

    And register it in Startup.ConfigureServices with AddJsonOptions, eg :

    services.AddControllers()                    
            .AddJsonOptions(options=>
                options.JsonSerializerOptions.Converters.Add(new TimeSpanToStringConverter()));
    
    
    0 讨论(0)
  • 2021-02-13 23:46

    My solution is to use custom converter, but with explicitly specified standard non culture-sensitive TimeSpan format specifier .

    public class JsonTimeSpanConverter : JsonConverter<TimeSpan>
    {
        public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            return TimeSpan.ParseExact(reader.GetString(), "c", CultureInfo.InvariantCulture);
        }
    
        public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options)
        {
            writer.WriteStringValue(value.ToString("c", CultureInfo.InvariantCulture));
        }
    }
    

    Then register it in the Startup for the HostBuilder:

    public class Startup
    {
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            ...
            services
                ...
                .AddJsonOptions(opts =>
                {
                    opts.JsonSerializerOptions.Converters.Add(new JsonTimeSpanConverter());                    
                });
            ...
        }
    }
    
    0 讨论(0)
  • 2021-02-13 23:52

    Not sure if it is the nullable TimeSpan that I am using, but even with .NET 5.0 I am still not able to deserialize a TimeSpan when using System.Text.Json.

    In any case, here is a solution that I have used in several models for custom serialization. Note that the [NotMapped] attribute is only required if you are planning to use the class as a database model.

    using System.Text.Json.Serialization;
    using System.ComponentModel.DataAnnotations.Schema;
    
    // Add your namespace and class declarations
    
    [JsonIgnore]
    public TimeSpan? MyTimeSpan { get; set; }
    
    [NotMapped]
    public long MyTimeSpanSerializer
    {
        get => MyTimeSpan?.Ticks ?? -1;
        set => MyTimeSpan = value >= 0 ? new TimeSpan(value) : (TimeSpan?)null;
    }
    
    0 讨论(0)
提交回复
热议问题