Azure / azure-cosmos-dotnet-v2

Contains samples and utilities relating to the Azure Cosmos DB .NET SDK
MIT License
579 stars 837 forks source link

Unable to use camel case in JSON when C# types use pascal case #317

Open Liversage opened 7 years ago

Liversage commented 7 years ago

I want to use a custom JsonSerializerSettings to control the format of the JSON stored in CosmosDB. In particular I want to use CamelCasePropertyNamesContractResolver to ensure that the JSON is camel cased even though the C# types use pascal case.

With the latest releases of the Microsoft.Azure.DocumentDB NuGet package it is now possible to provide a JsonSerializerSettings when creating a DocumentClient. However, in some cases the camel case versus pascal case issue is still a problem.

I experience two distinct problems but I have taken the liberty to include both in this report.

I use two document classes:

public class TestDocument
{
    public string Id { get; set; }
    public int Number { get; set; }
    public EmbeddedDocument Embedded { get; set; }
}

public class EmbeddedDocument
{
    public string Text { get; set; }
}

Here is code to create an document in CosmosDB based on these classes:

var jsonSerializerSettings = new JsonSerializerSettings {
    ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var client = new DocumentClient(
    new Uri("https://localhost:8081/"),
    "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
    jsonSerializerSettings);
var database = new Database { Id = "TestDatabase" };
await client.CreateDatabaseIfNotExistsAsync(database);
var collection = new DocumentCollection { Id = "TestCollection" };
var databaseUri = UriFactory.CreateDatabaseUri(database.Id);
await client.CreateDocumentCollectionIfNotExistsAsync(databaseUri, collection);
var testDocument = new TestDocument {
    Id = "Alpha",
    Number = 42,
    Embedded = new EmbeddedDocument { Text = "Beta" }
};
var collectionUri = UriFactory.CreateDocumentCollectionUri(database.Id, collection.Id);
await client.CreateDocumentAsync(collectionUri, testDocument);

The JSON in CosmosDB is properly camel cased as expected (I have removed the underscore prefixed properties for clarity):

{
    "id": "Alpha",
    "number": 42,
    "embedded": {
        "text": "Beta"
    }
}

My first problem is that querying using LINQ does not work:

var query = client.CreateDocumentQuery<TestDocument>(collectionUri)
    .Where(document => document.Number == 42)
    .AsDocumentQuery();
var results = new List<TestDocument>();
while (query.HasMoreResults)
    results.AddRange(await query.ExecuteNextAsync<TestDocument>());

At this point results contains no elements and not one element as expected. The reason is that the LINQ provider does not understand that the property to use in the query is number and not Number.

My second problem occurs when I want to do a partial update of a document. It is possible to avoid a complete deserialization and serializtion of the JSON document by using the following code:

var documentUri = UriFactory.CreateDocumentUri(database.Id, collection.Id, "Alpha");
var response = await client.ReadDocumentAsync(documentUri);
var embedded = response.Resource.GetPropertyValue<EmbeddedDocument>("embedded");
embedded.Text = "Gamma";
response.Resource.SetPropertyValue("embedded", embedded);
await client.ReplaceDocumentAsync(response.Resource);

Notice that only EmbeddedDocument is deserialized and then serialized again after it has been modified.

Unfortunately, the resulting JSON in CosmosDB after the code above has executed is the following:

{
    "id": "Alpha",
    "number": 42,
    "embedded": {
        "Text": "Gamma"
    }
}

Notice how the embedded.text property has changed case to embedded.Text (from camel case to pascal case). The reason is that the Document class which is the value of response.Resource does not use the custom JsonSerializerSettings.

sehcheese commented 4 years ago

@j82w In my LINQ to SQL query for Cosmos, I'm getting PascalCased properties, which do not conform to my JsonSerializerSettings. I'm using a custom CosmosClientOptions.Serializer, which excludes me from using CosmosClientOptions.SerializerOptions (when I attempted using both, I got a runtime exception that they cannot both be set). The fix in PR 716, relies on using CosmosClientOptions.SerializerOptions, which I cannot switch to because of other custom serialization settings I'm using that are not available there. It seems LINQ to SQL for Cosmos will not use the serializer from CosmosClientOptions. Is this correct? Does what I've said make sense?

Liversage commented 4 years ago

@sehcheese Based on the wording on your comment it seems to apply to the v3 SDK. However, your have written your comment on an issue in the v2 SDK.

Is your issue (which really is a question) related to v3 #810?

sehcheese commented 4 years ago

@Liversage Thanks, you're right that #810 more specifically details what I'm trying to say, and I am on the v3 SDK. Any further discussion should ensue in #810.

quico14 commented 3 years ago

Is this not working yet?

j82w commented 3 years ago

This is supported in the .NET v3 SDK.

sujit-warrier-maersk commented 1 year ago

I still got this problem in v3.30.0