Unable to use camel case in JSON when C# types use pascal case
See original GitHub issueOne of the shortcomings of the v2 SDK was that when using a custom JSON serializer to convert the pascal cased C# properties to camel case JSON then the LINQ provider would stop working. I raised an issue in the v2 repository about this two years ago.
It seems that this is still a problem in the v3 SDK. Let me walk you through how I tested this. A custom serializer is needed:
class CamelCaseCosmosSerializer : CosmosSerializer
{
private static readonly Encoding Encoding = new UTF8Encoding(false, true);
private static readonly JsonSerializer Serializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
public override T FromStream<T>(Stream stream)
{
using (stream)
{
if (typeof(Stream).IsAssignableFrom(typeof(T)))
return (T)(object)(stream);
using (StreamReader streamReader = new StreamReader(stream))
using (JsonTextReader jsonTextReader = new JsonTextReader(streamReader))
return Serializer.Deserialize<T>(jsonTextReader);
}
}
public override Stream ToStream<T>(T input)
{
var stream = new MemoryStream();
using (StreamWriter streamWriter = new StreamWriter(stream, encoding: Encoding, bufferSize: 1024, leaveOpen: true))
using (JsonWriter writer = new JsonTextWriter(streamWriter))
{
writer.Formatting = Newtonsoft.Json.Formatting.None;
Serializer.Serialize(writer, input);
writer.Flush();
streamWriter.Flush();
}
stream.Position = 0;
return stream;
}
}
The document type (notice that the Id
property is named using pascal case):
class Document
{
public string Id { get; set; }
public string Foo { get; set; }
}
Code to create a document (using the emulator):
var client = new CosmosClientBuilder("AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==")
.WithCustomSerializer(new CamelCaseCosmosSerializer())
.Build();
var database = (await client.CreateDatabaseIfNotExistsAsync("Test")).Database;
var container = (await database.CreateContainerIfNotExistsAsync("Test", "/id")).Container;
var id = new Random().Next(1_000_000);
await container.CreateItemAsync(new Document { Id = id.ToString(), Foo = "Bar" });
This works because the custom serializer emits JSON with a lower case id
property.
Unfortunately, creating a LINQ query doesn’t take this into account:
var queryable = container.GetItemLinqQueryable<Document>().Where(d => d.Foo == "Bar");
Console.WriteLine(queryable.ToString());
Printing queryable
results in the following:
{"query":"SELECT VALUE root FROM root WHERE (root[\"Foo\"] = \"Bar\") "}
Notice how the generated query uses pascal cased Foo
and not camel cased foo
as the property name.
It seems that the LINQ provider in the new SDK (like the old) assumes that .NET property names are mapped to JSON property names without any case conversion making it impossible to use the LINQ provider while at the same time using a JSON serializer that conventionally maps C# pascal case to JSON camel case. I can see that this problem is not easy to solve generally so I would at least like to know if this is a scenario you intend to support in the future.
Interestingly, the query works if I add [JsonProperty("foo")]
to the Foo
property so there must be some attempt in the LINQ provider to guess how property names are mapped when converting to JSON.
SDK version: 3.0.0, OS version: Windows
Issue Analytics
- State:
- Created 4 years ago
- Reactions:2
- Comments:8 (4 by maintainers)
Top GitHub Comments
@buyckkdelaware It’s released now in Preview 2. Works for me! Thanks guys, this came just in time 🥇
Is it possible to create a new preview version with this fix included? It’s not included in the current 3.2.0-preview version on NuGet. If not, any release data for 3.2.0? Thank you.