nodatime / nodatime.serialization

Serialization projects for Noda Time

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

"JsonException: The JSON value could not be converted to NodaTime.Instant" NodaTime issue with ASP.NET Core 3.1 Razor Page web application

rbiq opened this issue · comments

In ASP.NET Core 3.1 Razor Page pure front end web application I received the below error.

Installed the following packages:

<PackageReference Include="System.Text.Json" Version="4.7.2" />
<PackageReference Include="EnumExtensions.System.Text.Json" Version="1.0.0" />
<PackageReference Include="NodaTime" Version="3.0.1" />
<PackageReference Include="NodaTime.Serialization.SystemTextJson" Version="1.0.0" />

Also set this in Startup:

services.AddRazorPages()
  .AddJsonOptions(options =>
  {
      // options.JsonSerializerOptions.PropertyNamingPolicy = null;
      // options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
      // options.JsonSerializerOptions.DictionaryKeyPolicy = null;

      options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverterWithAttributeSupport(null, true, true, true, true));
      //options.JsonSerializerOptions.IgnoreNullValues = true;
      options.JsonSerializerOptions.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
      options.JsonSerializerOptions.Converters.Add(NodaConverters.IntervalConverter);
      options.JsonSerializerOptions.Converters.Add(NodaConverters.InstantConverter);
  })
  .AddRazorPagesOptions(options =>
  {
      options.Conventions.AddPageRoute("/Login", "");
  });

JsonException: The JSON value could not be converted to NodaTime.Instant. Path: $.data[0].created_at | LineNumber: 0 | BytePositionInLine: 261.
System.Text.Json.ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type propertyType)
System.Text.Json.JsonPropertyInfoNotNullable<TClass, TDeclaredProperty, TRuntimeProperty, TConverter>.OnRead(ref ReadStack state, ref Utf8JsonReader reader)
System.Text.Json.JsonPropertyInfo.Read(JsonTokenType tokenType, ref ReadStack state, ref Utf8JsonReader reader)
System.Text.Json.JsonSerializer.ReadCore(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack readStack)
System.Text.Json.JsonSerializer.ReadCore(Type returnType, JsonSerializerOptions options, ref Utf8JsonReader reader)
System.Text.Json.JsonSerializer.Deserialize(string json, Type returnType, JsonSerializerOptions options)
System.Text.Json.JsonSerializer.Deserialize(string json, JsonSerializerOptions options)

Here's the snippet of data it's trying to deserialize. If I switch from Instant to DateTimeOffset, it works "instantly" (pun intended :D)

{
    "data": [
        {
            "created_at": "2020-08-09T22:10:26.274672Z",
            "updated_at": "2020-08-13T02:22:02.640871Z",
        }
    ],
    "page": 1,
    "size": 20,
    "count": 1,
    "total": 1,
    "success": true,
    "message": null
}

Note: this json data is a result of serialization of an object that does include CreatedAt & UpdatedAt properties of the type (NodaTime)Instant. I confirm it works nicely with an asp.net core 3.1 mvc api application.

Not sure why it's not working.

Stack Overflow reference:
https://stackoverflow.com/questions/64356378/jsonexception-the-json-value-could-not-be-converted-to-nodatime-instant-nodat

You shouldn't need to add the InstantConverter and IntervalConverter again - those should be configured by ConfigureForNodaTime. I wouldn't expect that to break anything though.

Will look when I get a chance, although it might be a while.

Are you able to separate this out from ASP.NET Core by writing a console app that tries to deserialize the same JSON using System.Text.Json? That would make it easier to diagnose.

Sure. I can throw together something like that very quickly. Should I attach it here or provide you a gh link?

Either of those approaches works for me - anything so I don't need to do the fiddly routing bits!

Hi @jskeet I'm very sorry to waste your time earlier. So after doing some research & ultimately remembering/realizing that JsonSerializerOptions cannot be set at global in the current version of System.Text.Json I was finally able to get it working as expected by building options right where I need them. Here's the code snippet in case anyone gets stuck like me in the future.

var jsonString = "{\"data\":[{\"id\":\"f606942c-4740-46a7-be6f-66ceb38c530b\",\"created_at\":\"2020-08-09T22:10:26.274672Z\",\"updated_at\":\"2020-08-13T02:22:02.640871Z\"}],\"page\":1,\"size\":20,\"count\":1,\"total\":1,\"success\":true,\"message\":null }";

JsonSerializerOptions options = new JsonSerializerOptions();
options.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
options.Converters.Add(new JsonStringEnumConverterWithAttributeSupport(null, true, true, true, true));

var response = JsonSerializer.Deserialize<Response>(jsonString, options);




public enum SampleType
    {
        TYPE_0,
        TYPE_1
    }

    public class Sample
    {
        [JsonProperty("id")]
        public string Id { get; set; } = System.Guid.NewGuid().ToString();
        [JsonProperty("created_at")]
        public Instant CreatedAt { get; set; }
        [JsonProperty("updated_at")]
        public Instant UpdateAt { get; set; }

        [JsonProperty("type")]
        public SampleType Type { get; set; }
    }

    public class Response
    {
        [JsonProperty("data")]
        public IEnumerable<Sample> Data { get; set; }
        
        [JsonProperty("page")]
        public int Page { get; set; }
        
        [JsonProperty("size")]
        public int Size { get; set; }
        
        [JsonProperty("count")]
        public int Count { get; set; }
        
        [JsonProperty("total")]
        public int Total { get; set; }
        
        [JsonProperty("success")]
        public bool Success { get; set; }

        [JsonProperty("message")]
        public string Message { get; set; }
    }

Not a problem at all - thanks for raising and progressing it so clearly!