CosmosDB Serialization Overview
Sacha BruttinSerialization Settings
Since version 1.15.0, Azure’s CosmosDB SDK supports JsonSerializerSettings as a paramater. This gives us the ability to define custom contact serializer and how to handle null
values.
This is easy as this:
var serializerSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var client = new DocumentClient(endpointUrl, authorizationKey, serializerSettings);
The base class issue
When you create a document into CosmosDB you will realize that in addition to your own properties, some system properties prefixed with an underscore (_) are added and returned when quering the document:
Property | Purpose |
---|---|
_rid | System generated, unique, and hierarchical identifier of the resource |
_etag | etag of the resource required for optimistic concurrency control |
_ts | Last updated timestamp of the resource |
_self | Unique addressable URI of the resource |
Add the required id
propery that is either set by the user or auto-generated by the system if ommited.
In a way to reduce your typing, you could be tempted to inherits your documents from the Microsoft.Azure.Documents.Document class that it the class returned by most of the calls to the SDK. Unfortunatly, it doesn’t work in many fashions:
- Properties are not serialized if they are not marked with the
[JsonProperty]
attribute. JsonSerializerSettings
are not respected!
The persistance of this class
public class InheritFromDocument : Microsoft.Azure.Documents.Document
{
[JsonProperty]
public string Firstname { get; set; } = "Inherit";
[JsonProperty]
public string Lastname { get; set; } = "Document";
[JsonProperty]
public string NotDefinied { get; set; } = null;
}
will result into a JSON where our user-defined properties are not correctly camel cased despite the definied Contract Resolver and the null
values are serialized :
{
"Firstname": "Inherit",
"Lastname": "Document",
"NotDefinied": null,
"id": "dcf88cbf-d3ee-4558-b698-a94006d0d49c",
"_rid": "Ho1MAP0xtwqKAAAAAAAAAA==",
"_self": "dbs/Ho1MAA==/colls/Ho1MAP0xtwo=/docs/Ho1MAP0xtwqKAAAAAAAAAA==/",
"_etag": "\"00000000-0000-0000-c501-e64f859b01d3\"",
"_attachments": "attachments/",
"_ts": 1522069007
}
In another hand, if you inherit your documents from Microsoft.Azure.Documents.Resource you will get a correct serialization.
public class InheritFromResource : Microsoft.Azure.Documents.Resource
{
public string Firstname { get; set; } = "Inherit";
public string Lastname { get; set; } = "Resource";
public string NotDefinied { get; set; } = null;
}
{
"firstname": "Inherit",
"lastname": "Resource",
"id": "5e1211e8-4589-436a-bfa0-a17dea4accf8",
"_rid": "Ho1MAP0xtwqLAAAAAAAAAA==",
"_self": "dbs/Ho1MAA==/colls/Ho1MAP0xtwo=/docs/Ho1MAP0xtwqLAAAAAAAAAA==/",
"_etag": "\"00000000-0000-0000-c501-e6542d2801d3\"",
"_attachments": "attachments/",
"_ts": 1522069007
}
You can also create documents from your own POCO but you will have to handle the system properties by hand if you need to retrieve them.
public class POCO
{
public string Id { get; set; }
public string Firstname { get; set; } = "Po";
public string Lastname { get; set; } = "Co";
public string NotDefinied { get; set; } = null;
}
Summary
Don’t use Microsoft.Azure.Documents.Document
as your base class for storing document. It will not respect your settings!
Microsoft.Azure.Documents.Resource
is a far better alternative. POCO are as always a good fit. It doesn’t tie you to a specific implementation or SDK and you can easily create a base class that will handle the system properties.