Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Microsoft.Azure.Cosmos.Spatial.Point is not compatible with UseSystemTextJsonSerializerWithOptions or System.Text.Json #4744

Open
quinterojose opened this issue Oct 2, 2024 · 1 comment · May be fixed by #4801
Assignees
Labels
bug Something isn't working customer-reported Issue created by a customer good first issue Good for newcomers

Comments

@quinterojose
Copy link

Describe the bug
When creating an item using CreateItemAsync which contains a property of type Microsoft.Azure.Cosmos.Spatial.Point the property is not serialized properly. This only happens when using UseSystemTextJsonSerializerWithOptions or with a custom Serializer which uses System.Text.Json.

The following exception occurs:

System.NotSupportedException
  HResult=0x80131515
  Message=Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'Microsoft.Azure.Cosmos.Spatial.Point'. Path: $.location | LineNumber: 0 | BytePositionInLine: 96.
  Source=System.Text.Json
  StackTrace:
   at System.Text.Json.ThrowHelper.ThrowNotSupportedException(ReadStack& state, Utf8JsonReader& reader, NotSupportedException ex)
   at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
   at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
   at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 utf8Json, JsonTypeInfo`1 jsonTypeInfo, Nullable`1 actualByteCount)
   at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 json, JsonTypeInfo`1 jsonTypeInfo)
   at System.Text.Json.JsonSerializer.Deserialize[TValue](String json, JsonSerializerOptions options)
   at Microsoft.Azure.Cosmos.CosmosSystemTextJsonSerializer.FromStream[T](Stream stream)
   at Microsoft.Azure.Cosmos.CosmosJsonSerializerWrapper.FromStream[T](Stream stream)
   at Microsoft.Azure.Cosmos.CosmosSerializerCore.FromStream[T](Stream stream)
   at Microsoft.Azure.Cosmos.CosmosResponseFactoryCore.ToObjectpublic[T](ResponseMessage responseMessage)
   at Microsoft.Azure.Cosmos.CosmosResponseFactoryCore.<CreateItemResponse>b__8_0[T](ResponseMessage cosmosResponseMessage)
   at Microsoft.Azure.Cosmos.CosmosResponseFactoryCore.ProcessMessage[T](ResponseMessage responseMessage, Func`2 createResponse)
   at Microsoft.Azure.Cosmos.CosmosResponseFactoryCore.CreateItemResponse[T](ResponseMessage responseMessage)
   at Microsoft.Azure.Cosmos.ContainerCore.<CreateItemAsync>d__56`1.MoveNext()
   at Microsoft.Azure.Cosmos.ClientContextCore.<RunWithDiagnosticsHelperAsync>d__40`1.MoveNext()
   at Microsoft.Azure.Cosmos.ClientContextCore.<OperationHelperWithRootTraceAsync>d__30`1.MoveNext()
   at Program.<<Main>$>d__0.MoveNext() in C:\Users\jquintero\source\repos\AzureCosmosSpatialSample\AzureCosmosSpatialSample\Program.cs:line 22

Inner Exception 1:
NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'Microsoft.Azure.Cosmos.Spatial.Point'.

Using PatchItemAsync to set a value of type Microsoft.Azure.Cosmos.Spatial.Point updates the document with an improperly serialized value.

To Reproduce
See the following code snippet to reproduce:

using AzureCosmosSpatialSample;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.Cosmos.Spatial;
using Newtonsoft.Json;
using System.Text.Json;

var client = new CosmosClient(
    "Azure Cosmos Db Emulator Connection String",
    new CosmosClientOptions
    {
        UseSystemTextJsonSerializerWithOptions = new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        }
    });

Database database = await client.CreateDatabaseIfNotExistsAsync("AzureCosmosSpatialSample");

Container container = await database.CreateContainerIfNotExistsAsync("spatial-items", "/id");

// Exception occurs here due to CreateItemAsync trying to deserialize response.
SampleSpatialItem result = await container.CreateItemAsync<SampleSpatialItem>(new()
{
    Id = Guid.NewGuid().ToString(),
    Name = "Latitude and Longitude Sample",
    Location = new Point(1, 1)
});

namespace AzureCosmosSpatialSample
{
    public record SampleSpatialItem
    {
        [JsonProperty("id")]
        public string? Id { get; init; }

        public string? Name { get; init; }

        public Point? Location { get; init; }
    }
}

Expected behavior
Property of type Microsoft.Azure.Cosmos.Spatial.Point is serialized as follows:

"location": {
        "type": "Point",
        "coordinates": [
            1,
            1
        ]
    }

Actual behavior
Property of type Microsoft.Azure.Cosmos.Spatial.Point is serialized as follows:

"location": {
        "position": {
            "coordinates": [
                1,
                1
            ],
            "longitude": 1,
            "latitude": 1,
            "altitude": null
        },
        "crs": {
            "type": 0
        },
        "type": 0,
        "boundingBox": null,
        "additionalProperties": {}
    }

Environment summary
SDK Version: 3.43.1 on .NET 8
OS Version: Windows 11

Additional context
The same exception occurs if an item was previously serialized as:

"location": {
        "type": "Point",
        "coordinates": [
            1,
            1
        ]
    }

Then the item is retrieved using ReadItemAsync.

After some investigation it seems that Microsoft.Azure.Cosmos.Spatial.Point uses Newtonsoft.Json for serialization. I have created a custom System.Text.Json.Serialization.JsonConverter replicating the functionality of the various built-in converters to get around the issue when creating or reading the item, however, when using PatchItemAsync to set a value of Microsoft.Azure.Cosmos.Spatial.Point the converter is ignored because PatchOperation is Newtonsoft.Json all the way down and the value is serialized as follows:

"location": {
        "position": {
            "coordinates": [
                1,
                1
            ],
            "longitude": 1,
            "latitude": 1,
            "altitude": null
        },
        "crs": {
            "type": 0
        },
        "type": 0,
        "boundingBox": null,
        "additionalProperties": {}
    }

I'm happy to provide any additional information.

@kirankumarkolli
Copy link
Member

@quinterojose thank you for reporting it. Its a bug, we will work on the fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working customer-reported Issue created by a customer good first issue Good for newcomers
Projects
None yet
3 participants