How to fix: Unable to cast object of type 'MongoDB.Bson.BsonArray' to type 'MongoDB.Bson.BsonBoolean'

Viewed 2632

I'm currently working on an API GET method with a MongoDb $GeoWithin query. If I check the code with breakpoints, I see that my query is returning a result. But when my application is supposed to return a response, I get an error (500) response in Postman.

I just can't seem to figure out what is going wrong. I see "Unable to cast object of type 'MongoDB.Bson.BsonArray' to type 'MongoDB.Bson.BsonBoolean'." in the stacktrace, but I don't see anything wrong with my model/MongoDb document.

Disclaimer: my current code is quite messy, but I was just trying to get this thing to work before refactoring it.

My Model

public class Bag3DMember
    {
        [BsonId]
        [DataMember]
        [BsonElement("_id")]
        public string _Id { get; set; }

        [DataMember]
        [BsonElement("type")]
        public string Type { get; set; }

        [BsonElement("geometry")]
        public Geometry Geometry { get; set; }

        [BsonElement("geometry_name")]
        public string GeometryName { get; set; }

        [BsonElement("properties")]
        public Properties Properties { get; set; }

    }

    public class Geometry
    {
        [BsonElement("type")]
        public string Type { get; set; }

        [BsonElement("coordinates")]
        public BsonArray Coordinates { get; set; }
    }

    public class Properties
    {
        [BsonElement("gid")]
        public int Gid { get; set; }

        [BsonElement("identificatie")]
        public string Identificatie { get; set; }

        [BsonElement("aanduidingrecordinactief")]
        public bool AanduidingrecordInactief { get; set; }

        [BsonElement("aanduidingrecordcorrectie")]
        public int AanduidingrecordCorrectie { get; set; }

        [BsonElement("officieel")]
        public bool Officieel { get; set; }

        [BsonElement("inonderzoek")]
        public bool InOnderzoek { get; set; }

        [BsonElement("documentnummer")]
        public string DocumentNummer { get; set; }

        [BsonElement("documentdatum")]
        public string DocumentDatum { get; set; }

        [BsonElement("bouwjaar")]
        public string Bouwjaar { get; set; }

        [BsonElement("begindatumtijdvakgeldigheid")]
        public string BeginDatumTijdVakGeldigheid { get; set; }

        [BsonElement("einddatumtijdvakgeldigheid")]
        public string EindDatumTijdVakGeldigheid { get; set; }

        [BsonElement("gemeentecode")]
        public string GemeenteCode { get; set; }

        [BsonElement("ground-000")]
        public decimal? Ground000 { get; set; }

        [BsonElement("ground-010")]
        public decimal? Ground010 { get; set; }

        [BsonElement("ground-020")]
        public decimal? Ground020 { get; set; }

        [BsonElement("ground-030")]
        public decimal? Ground030 { get; set; }

        [BsonElement("ground-040")]
        public decimal? Ground040 { get; set; }

        [BsonElement("ground-050")]
        public decimal? Ground050 { get; set; }

        [BsonElement("roof-025")]
        public decimal? Roof025 { get; set; }

        [BsonElement("roof-050")]
        public decimal? Roof050 { get; set; }

        [BsonElement("roof-075")]
        public decimal? Roof075 { get; set; }

        [BsonElement("roof-090")]
        public decimal? Roof090 { get; set; }

        [BsonElement("roof-095")]
        public decimal? Roof095 { get; set; }

        [BsonElement("roof-099")]
        public decimal? Roof099 { get; set; }

        [BsonElement("rmse-025")]
        public decimal? Rmse025 { get; set; }

        [BsonElement("rmse-050")]
        public decimal? Rmse050 { get; set; }

        [BsonElement("rmse-075")]
        public decimal? Rmse075 { get; set; }

        [BsonElement("rmse-090")]
        public decimal? Rmse090 { get; set; }

        [BsonElement("rmse-095")]
        public decimal? Rmse095 { get; set; }

        [BsonElement("rmse-099")]
        public decimal? Rmse099 { get; set; }

        [BsonElement("roof_flat")]
        public bool RoofFlat { get; set; }

        [BsonElement("nr_ground_pts")]
        public int NrGroundPts { get; set; }

        [BsonElement("nr_roof_pts")]
        public int NrRoofPts { get; set; }

        [BsonElement("ahn_file_date")]
        public string AhnFileDate { get; set; }

        [BsonElement("ahn_version")]
        public int AhnVersion { get; set; }

        [BsonElement("height_valid")]
        public bool HeightValid { get; set; }

        [BsonElement("tile_id")]
        public string TileId { get; set; }

        [BsonElement("bbox")]
        public BsonArray[] BoundingBox { get; set; }
    }

My controller method:

[HttpGet]
        public ActionResult<List<Bag3DMember>> Get(string coordinates, double radius)
        {
            if(coordinates == null || radius == 0)
            {
                return StatusCode(400, "Paramaters: 'coordinates' and/or 'radius' are not found.");
            }

            var splittedCoordinates = coordinates.Split(',');

            if(splittedCoordinates.Length != 2)
            {
                return StatusCode(406, "Coordinatesformat not accepted.");
            }

            var formattedCoordinates = Array.ConvertAll(splittedCoordinates, s => double.Parse(s, CultureInfo.InvariantCulture));

            FieldDefinition<Bag3DMember> field = "bbox";
            var results = _bag3DService.GetBySpatialQuery(field, formattedCoordinates[0], formattedCoordinates[1], radius);
            var jsonResults = Json(results);
            return Json(results);
        }

The database service method

public List<Bag3DMember> GetBySpatialQuery(FieldDefinition<Bag3DMember> field, double x, double y, double radius)
        {
            //var filter = Builders<Bag3DMember>.Filter.GeoWithinBox(field, (x - (radius/2)), (y - (radius / 2)), (x + (radius / 2)), (y + (radius / 2)));
            BsonArray lowerLeftDoc = new BsonArray(new[] { 0, 0 });
            BsonArray upperRightDoc = new BsonArray(new[] { 10000000, 10000000 });
            BsonArray boundingBox = new BsonArray(new[] { lowerLeftDoc, upperRightDoc });

            BsonDocument locBox = new BsonDocument { { "$box", boundingBox } };
            BsonDocument locDoc = new BsonDocument { { "$geoWithin", locBox } };
            BsonDocument queryDoc = new BsonDocument { { "properties.bbox", locDoc } };

            var results = _Bag3DMembers.Find(new QueryDocument(queryDoc)).ToList();

            return results;
        }

So in Visual Studio it returns the query successfully. As you can see, it returns one document from the database: results

JSON object:

{
    "$type": "System.Collections.Generic.List<EnveoApi.Bag3DMember>",
    "$values": [
        {
            "$type": "EnveoApi.Bag3DMember",
            "_Id": "pand3d.8575483",
            "Type": "Feature",
            "Geometry": {
                "$type": "EnveoApi.Geometry",
                "Type": "Polygon",
                "Coordinates": {
                    "$type": "MongoDB.Bson.BsonArray",
                    "$values": [
                        {
                            "$type": "MongoDB.Bson.BsonArray",
                            "$values": [
                                {
                                    "$type": "MongoDB.Bson.BsonArray",
                                    "$values": [
                                        108665.593,
                                        447232.925,
                                        0
                                    ]
                                },
                                {
                                    "$type": "MongoDB.Bson.BsonArray",
                                    "$values": [
                                        108667.648,
                                        447229.102,
                                        0
                                    ]
                                },
                                {
                                    "$type": "MongoDB.Bson.BsonArray",
                                    "$values": [
                                        108676.807,
                                        447234.217,
                                        0
                                    ]
                                },
                                {
                                    "$type": "MongoDB.Bson.BsonArray",
                                    "$values": [
                                        108674.334,
                                        447238.579,
                                        0
                                    ]
                                },
                                {
                                    "$type": "MongoDB.Bson.BsonArray",
                                    "$values": [
                                        108665.593,
                                        447232.925,
                                        0
                                    ]
                                }
                            ]
                        }
                    ]
                }
            },
            "GeometryName": "geovlak",
            "Properties": {
                "$type": "EnveoApi.Properties",
                "Gid": 8575483,
                "Identificatie": "0513100011121832",
                "AanduidingrecordInactief": "false",
                "AanduidingrecordCorrectie": 0,
                "Officieel": "false",
                "InOnderzoek": "false",
                "DocumentNummer": "BAGAV1776",
                "DocumentDatum": "2018-08-15Z",
                "Bouwjaar": "1900-01-01Z",
                "BeginDatumTijdVakGeldigheid": "2018-08-14T22:00:00Z",
                "EindDatumTijdVakGeldigheid": null,
                "GemeenteCode": "0513",
                "Ground000": -0.44,
                "Ground010": -0.42,
                "Ground020": -0.41,
                "Ground030": -0.41,
                "Ground040": -0.4,
                "Ground050": -0.39,
                "Roof025": 2.72,
                "Roof050": 2.81,
                "Roof075": 3.03,
                "Roof090": 4.76,
                "Roof095": 7.04,
                "Roof099": 9.72,
                "Rmse025": 0.87,
                "Rmse050": 0.62,
                "Rmse075": 0.62,
                "Rmse090": 0.62,
                "Rmse095": 0.62,
                "Rmse099": 0.62,
                "RoofFlat": "false",
                "NrGroundPts": 12,
                "NrRoofPts": 1247,
                "AhnFileDate": "2014-02-25T23:00:00Z",
                "AhnVersion": 3,
                "HeightValid": "true",
                "TileId": "38an2",
                "BoundingBox": {
                    "$type": "MongoDB.Bson.BsonArray[]",
                    "$values": [
                        {
                            "$type": "MongoDB.Bson.BsonArray",
                            "$values": [
                                108665.593,
                                447229.102
                            ]
                        },
                        {
                            "$type": "MongoDB.Bson.BsonArray",
                            "$values": [
                                108676.807,
                                447238.579
                            ]
                        }
                    ]
                }
            }
        }
    ]
}

I don't get any error in Visual Studio 2019. I only get the error in Postman (stacktrace):

System.InvalidCastException: Unable to cast object of type 'MongoDB.Bson.BsonArray' to type 'MongoDB.Bson.BsonBoolean'.
   at get_AsBoolean(Object )
   at System.Text.Json.JsonPropertyInfoNotNullable`4.OnWrite(WriteStackFrame& current, Utf8JsonWriter writer)
   at System.Text.Json.JsonPropertyInfo.Write(WriteStack& state, Utf8JsonWriter writer)
   at System.Text.Json.JsonSerializer.HandleObject(JsonPropertyInfo jsonPropertyInfo, JsonSerializerOptions options, Utf8JsonWriter writer, WriteStack& state)
   at System.Text.Json.JsonSerializer.WriteObject(JsonSerializerOptions options, Utf8JsonWriter writer, WriteStack& state)
   at System.Text.Json.JsonSerializer.Write(Utf8JsonWriter writer, Int32 originalWriterDepth, Int32 flushThreshold, JsonSerializerOptions options, WriteStack& state)
   at System.Text.Json.JsonSerializer.WriteAsyncCore(Stream utf8Json, Object value, Type inputType, JsonSerializerOptions options, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Mvc.Infrastructure.SystemTextJsonResultExecutor.ExecuteAsync(ActionContext context, JsonResult result)
   at Microsoft.AspNetCore.Mvc.Infrastructure.SystemTextJsonResultExecutor.ExecuteAsync(ActionContext context, JsonResult result)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeResultFilters>g__Awaited|27_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

HEADERS
=======
Accept: */*
Accept-Encoding: gzip, deflate
Cache-Control: no-cache
Connection: keep-alive
Host: localhost:44348
User-Agent: PostmanRuntime/7.19.0
Postman-Token: c23588a0-0e6b-46f8-834c-93dbfc6134eb

Edit 1:

It seems that it works if I comment out all the nested "properties" and "coordinates" in "geometry".

So apparently "coordinates" gives me a issues. But there also documents in "properties" that is giving me issues. But I still don't understand why it's trying to cast BsonArray (which the coordinates are) to a boolean?

Also, I am now just returning the result, instead of Json(results). But that didn't make any difference.

Edit 2: The "coordinates" work if I make it dynamic. But I think that's bad practise right?

 public class Geometry
{
    [BsonElement("type")]
    public string Type { get; set; }

    [BsonElement("coordinates")]
    public dynamic Coordinates { get; set; }
}
2 Answers

I had this problem and my workaround was to change my collection generic to and all was ok and what ever model structure will work. Example: DB.GetCollection("collection name");

Currently fixed the problem with the original poster suggested. using dynamic instead of BsonDocument, BsonArray or which ever your type is not working. switch between dynamic and voila!

Related