I'm migrating a project from Microsoft.AspNetCore.Mvc.NewtonsoftJson
to System.Text.Json
. I couldn't do this in .NET Core 3.1 due to dotnet/runtime#38056 as some of my models contain properties of type IDictionary<int, T>
, something like:
public class MyClass {
public int ID { get; set; }
public IDictionary<int, ThingClass> Things { get; set; }
}
In .NET 5 this now works for serialisation, so this works:
[HttpGet("{id}")]
public async Task<MyClass> Get(int id) ...
However, when I send data back:
[HttpPost]
public async Task Update([FromBody] MyClass changes) ...
With a body like (note that the key of "things"
is numbers, not strings}:
{
"id": 789,
"things": {
123: { ... },
456: { ... }
}
}
I get an error:
Unable to cast object of type
'System.Collections.Generic.Dictionary`2[System.String,MyNamespace.ThingClass]'
to type'System.Collections.Generic.IDictionary`2[System.Int32,MyNamespace.ThingClass]'
.
at System.Text.Json.Serialization.Converters.IDictionaryOfTKeyTValueConverter`3.Add(TKey key, TValue& value, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.Serialization.Converters.DictionaryDefaultConverter`3.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, TCollection& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.Serialization.JsonConverter`1.ReadCoreAsObject(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.JsonSerializer.ReadCore[TValue](JsonConverter jsonConverter, Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.JsonSerializer.ReadCore[TValue](JsonReaderState& readerState, Boolean isFinalBlock, ReadOnlySpan`1 buffer, JsonSerializerOptions options, ReadStack& state, JsonConverter converterBase)
at System.Text.Json.JsonSerializer.ReadAsync[TValue](Stream utf8Json, Type returnType, JsonSerializerOptions options, CancellationToken cancellationToken)
This looks like something is pre-parsing the body first (and assuming the key is a string) and then the step from Dictionary<string, ThingClass>
to IDictionary<int, ThingClass>
fails, but then the keys should never have been parsed as string
in the first place.
What is doing this deserialisation wrong?
How do I configure what [FromBody]
uses? Why isn't it using the same settings as the deserialisation?
Is this a bug in .NET 5's System.Text.Json
(it seems way too basic, someone would have spotted it way before this) and if so is there any way around it?
Note: This question is not about whether numeric keys are in the RFC8259
standard (they aren't). They do work in actual JS and are in lots of JSON in the wild and so do sometimes need to be output and parsed. System.Text.Json
claims that it supports non-string dictionary keys, I want to get that working. "Use NewtonsoftJson
" or "you're not spec compliant" will not be accepted as answers.