Search code examples
jsonserializationazure-cosmosdbazure-cosmosdb-sqlapi

Unable to save a JsonElement type field to Cosmos DB


When I save a model into No sql cosmos db instance using entity framework core. It does not persist the JsonElement JobParams whereas the jobParamsJson string is good. I require the hashmap like object JobParams to be actually persisted in the db as it is being consumed by other clients too.

The JobParams field in the cosmosdb document created looks like

{  
   ValueKind:1   
}

Solution

  • The Cosmos v3 SDK uses Newtonsoft.Json as serializer by default, while JsonElement is an object that is used by System.Text.Json. Since it is not decorated with a custom JsonConverter from Newtonsoft.Json it will serialize it with its properties which causes it to display it like that.

    If you want to make use the System.Text.Json you could create a serializer for it. Here's example from Github:

    public class CosmosSystemTextJsonSerializer : CosmosSerializer
    {
        private readonly JsonObjectSerializer systemTextJsonSerializer;
    
        public CosmosSystemTextJsonSerializer(JsonSerializerOptions jsonSerializerOptions)
        {
            systemTextJsonSerializer = new JsonObjectSerializer(jsonSerializerOptions);
        }
    
        public override T FromStream<T>(Stream stream)
        {
            using (stream)
            {
                if (stream.CanSeek
                       && stream.Length == 0)
                {
                    return default;
                }
    
                if (typeof(Stream).IsAssignableFrom(typeof(T)))
                {
                    return (T)(object)stream;
                }
    
                return (T)systemTextJsonSerializer.Deserialize(stream, typeof(T), default);
            }
        }
    
        public override Stream ToStream<T>(T input)
        {
            var streamPayload = new MemoryStream();
            systemTextJsonSerializer.Serialize(streamPayload, input, input.GetType(), default);
            streamPayload.Position = 0;
            return streamPayload;
        }
    }
    
    

    Then just add it to your options when creating the CosmosClient instance:

    var client = new CosmosClient(accountEndpoint: uri, authKeyOrResourceToken: key, clientOptions: new()
    {
        Serializer = new CosmosSystemTextJsonSerializer(),
    });