Scenario :
I have a cosmosDb container stored in Azure.
I have this kind of data class that I read and write to it :
public class MyClass { public Guid Id { get; private set; } public string PartitionKey { get; private set; } [JsonConverter(typeof(MyTypeJsonConverter))] //[Newtonsoft.Json.JsonConverter(typeof(MyTypeNewtonsoftJsonConverter))] public MyType Custom { get; private set; } [JsonConstructor] // <-- I've tried with and without this public MyClass(Guid id, string pk, MyType custom) { Custom = custom; Id = id; PartitionKey = pk; } }
as you can see there's a custom type, MyType, that gets converted with a custom converter.
For test purposes, I wrote the converter both with Newtonsoft and System.Text:
public class MyTypeJsonConverter : JsonConverter<MyType> { public override bool CanConvert(Type objectType) { ... } public override void Write(...) { ... } public override Context Read(...) { ... } } public class ContextNewtonsoftJsonConverter : Newtonsoft.Json.JsonConverter { ... }
I know that MyType works and that the converters work because serialization and deserialization work as expected both with System.Text.Json and Newtonsoft.Json when I do just this :
Newtonsoft //var myClass = JsonConvert.DeserializeObject<MyClass>(someJson); //System.Text.Json var myClass2 = JsonSerializer.Deserialize<MyClass>(someJson, new JsonSerializerOptions() { IgnoreNullValues = false, PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
A similar deserialization happens too when I read and write objects from CosmosDb.
CosmosClientOptions options = new() { ConnectionMode = DebugHelper.DebugMode ? ConnectionMode.Gateway : ConnectionMode.Direct, SerializerOptions = new CosmosSerializationOptions { IgnoreNullValues = false, PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase }, }; var cosmosClient = CosmosClient .CreateAndInitializeAsync(connectionString, containersList, options) .GetAwaiter() .GetResult(); var container = cosmosClient .GetContainer("mydatabase", "mycontainer"); var items = container.GetItemLinqQueryable<MyType>(allowSynchronousQueryExecution: true); foreach (var item in items) { await container.DeleteItemAsync<MyType>(item.Id.ToString(), new PartitionKey(item.PartitionKey)); }
Problem :
the code just above works perfectly with the NewtonSoft version... ...But fails with the System.Text.Json version.
---- Newtonsoft.Json.JsonSerializationException : Error converting value "my string value" to type 'MyType'. -------- System.ArgumentException : Could not cast or convert from System.String to MyType.
That exception does NOT happen inside the Read
and Write
functons of the converter. It happens "beforehand". It's like the [JsonConverter(...)]
attribute is understood by JsonSerializer.Deserialize<Mytype>
but not by cosmosContainer.DoSomethingWithItem<MyType>
.
Again; it works with a plain serialization/desrialization, and it works with the CosmosClient when I use Newtonsoft.
Question :
- (before you dive into complex Json) Can you spot an obvious mistake?
- Do I need to register the custom JsonConverter, and if yes what's the most compact way of doing that? (Anything that doesn't require me to implement an entire custom Serializer to pass to CosmosDb... unless you can prove to me that there's a good explanation why Newtonsoft can do it without it but System.Text.Json can't)