Search code examples
azure-cosmosdbgeospatial

Microsoft.Azure.Cosmos.Spatial Error while deserializing the point coordinates


I am trying to store geospatial data in my cosmos db container named Trips and retrieve the items from my .net core web api (.net 5). I am using Microsoft.Azure.Cosmos (3.19.0) and have not configured anything explicitly for serializing. The save works but when I try to retrieve I am getting this error:

Newtonsoft.Json.JsonSerializationException: Failed to deserialize Geometry object because 'type' property is either absent or has invalid value.

Cosmos Client Instance:

   var options = new CosmosClientOptions()
    {
       AllowBulkExecution = true,
       SerializerOptions = new CosmosSerializationOptions()
       {
          PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase
       }
    };
    var cosmosClient = new CosmosClient(account, key, options);

Model:

public class Trip
    {
        [JsonPropertyName("id"), Required]
        public string Id { get; set; }

        [JsonPropertyName("vehicleId"), Required]
        public string VehicleId { get; set; }

        [JsonPropertyName("startDateTime"), Required]
        public DateTime StartDateTime { get; set; }

        [JsonPropertyName("endDateTime"), Required]
        public DateTime EndDateTime { get; set; }

        [JsonPropertyName("wayPoints"), Required]
        public List<WayPoint> WayPoints { get; set; }
    }

    public class WayPoint
    {
        [JsonPropertyName("timeStamp"), Required]
        public DateTime TimeStamp { get; set; }

        [JsonPropertyName("point"), Required]
        public Point Point { get; set; }
    }

Service:

public async Task<IEnumerable<Trip>> GetMultipleAsync(string vehicleId = "")
{
   var queryDefinition = new QueryDefinition($"Select * from c where c.vehicleId = \"{vehicleId}\"");
   var queryIterator = _container.GetItemQueryIterator<Trip>(queryDefinition);
    
   var trips = new List<Trip>();
   while(queryIterator.HasMoreResults)
   {
      var response = await queryIterator.ReadNextAsync();
      trips.AddRange(response.ToList());
   }
   return trips;
}

Controller:

using FileStream createStream = System.IO.File.Create(@"C:\repos\TripGenerator\trips.json");
await JsonSerializer.SerializeAsync(
 createStream,
 trips);

Json sample:

{
    "id": "a9153ca0-e171-4fe8-bcfe-733ac75f6b85",
    "vehicleId": "599abc63-eafb-4015-ac65-fc6aed48d9aa",
    "startDateTime": "2021-06-17T00:00:00Z",
    "endDateTime": "2021-06-17T23:55:00Z",
    "wayPoints": [
        {
            "timeStamp": "2021-06-17T00:00:00Z",
            "point": {
                "Position": {
                    "Coordinates": [
                        51.23156579100001,
                        -0.603818000999999
                    ],
                    "Longitude": 77.23156579100001,
                    "Latitude": 12.016038180009999,
                    "Altitude": null
                },
                "Crs": {
                    "Type": 0
                },
                "Type": 0,
                "BoundingBox": null,
                "AdditionalProperties": {}
            }
        },
        {
            "timeStamp": "2021-06-17T00:05:00Z",
            "point": {
                "Position": {
                    "Coordinates": [
                        51.23159449100001,
                        -0.01703846700999
                    ],
                    "Longitude": 77.23159449100001,
                    "Latitude": 12.603846700999998,
                    "Altitude": null
                },
                "Crs": {
                    "Type": 0
                },
                "Type": 0,
                "BoundingBox": null,
                "AdditionalProperties": {}
            }
        },
        ////////////
        {
            "timeStamp": "2021-06-17T23:55:00Z",
            "point": {
                "Position": {
                    "Coordinates": [
                        51.23980269100042,
                        -0.01961205490099
                    ],
                    "Longitude": 77.23980269100042,
                    "Latitude": 12.612054900999901,
                    "Altitude": null
                },
                "Crs": {
                    "Type": 0
                },
                "Type": 0,
                "BoundingBox": null,
                "AdditionalProperties": {}
            }
        }
    ],
}

Any help would be appreciated. Thanks!


Solution

  • The problem seems to be on the casing configuration you are using:

    PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase
    

    Your Json is showing that the "Type" property for the "Point" is there, but the casing in the error says "type" (lower case T).

    The Geometry type in the V3 SDK (https://github.com/Azure/azure-cosmos-dotnet-v3/blob/master/Microsoft.Azure.Cosmos/src/Spatial/Geometry.cs#L70) has Type defined as "type":

    [DataMember(Name = "type")]
    [JsonProperty("type", Required = Required.Always, Order = 0)]
    [JsonConverter(typeof(StringEnumConverter))]
    public GeometryType Type { get; private set; }
    

    So normally we expect it to be serialized/saved as "type", not "Type".

    Either the PropertyNamingPolicy made it be serialized as "Type" when the document was saved OR the document was saved with a different SDK / tool that serialized the geometry with "Type" instead.

    That is the reason this exception is being thrown.