XLVII
XLVII

Reputation: 141

How can I bind GeoJSON data to parameters in ASP.NET Core?

In GeoJSON the same attribute coordinates can be an array or array of arrays.

How can I define the same attribute name but different types for the same json object?

public class Geometry
{
    public string type { get; set; }

    [BsonElement("coordinates")]
    public List<List<List<double>>> coordinates { get; set; }

    [BsonElement("coordinates")]
    public List<List<List<List<double>>>> coordinates { get; set; }
}

coordinates can be 4d or 3d, how can I handle this variability?

example:

{
        "type": "Polygon",
        "coordinates": [
            [
                [
                    [
                        51.263632,
                        60.962938
                    ],
                    [
                        30.884274,
                        20.065517
                    ],
                    [
                        68.832044,
                        14.362602
                    ],
                    [
                        51.263632,
                        60.962938
                    ]
                ]
            ]
        ]
}

or

{
    "type": "MultiPolygon",
    "coordinates": [
        [
            [
                [
                    43.182162,
                    64.042209
                ],
                [
                    14.721334,
                    22.358269
                ],
                [
                    51.263632,
                    17.738761
                ],
                [
                    43.182162,
                    64.042209
                ]
            ]
        ],
        [
            [
                [
                    55.831419,
                    51.446822
                ],
                [
                    65.66973,
                    20.065517
                ],
                [
                    97.64424,
                    37.509124
                ],
                [
                    55.831419,
                    51.446822
                ]
            ]
        ]
    ]
}

My action is

[HttpPost]
public IActionResult<Geometry> Create(Geometry layer)
{
    _layerService.Create(layer);
    
    return NoContent();
}

Upvotes: 3

Views: 2672

Answers (1)

Panagiotis Kanavos
Panagiotis Kanavos

Reputation: 131581

Don't try to roll your own classes. This isn't something that can be done in a single step, and the classes you'll end up won't be easy to use for spatial scenarios. It's better to use common spatial classes and a library that can serialize them.

Even if you roll your own, it would be a lot easier to create custom JSON converters that map your Spatial classes to GeoJSON.

NetTopologySuite, the almost-de-facto standard for spatial data in .NET Core, has published the NetTopologySuite.IO.GeoJSON4STJ package that serializes NTS classes to GeoJSON using System.Text.Json. At 84K downloads it's not as popular as the 1.4M downloads of NetTopologySuite.IO.GeoJSON that uses JSON.NET, but it does the job.

The repo landing page shows how to use GeoJSON4STJ to bind GeoJSON payloads to Geometry parameters in classes, by adding a custom converter :

services.AddControllers()
  .AddJsonOptions(options => {
        options.JsonSerializerOptions.Converters.Add(
            new NetTopologySuite.IO.Converters.GeoJsonConverterFactory());
  });

The NTS classes are used by EF Core itself for Spatial data so it's a great idea to use these instead of creating new classes.

The landing page shows how to (de)serialize JSON text too:

var geoJson = "{\"type\":\"Point\",\"coordinates\":[0.0,0.0]}";
Geometry geometry;

var serializer = GeoJsonSerializer.Create();
using var stringReader = new StringReader(geoJson);
using var jsonReader = new JsonTextReader(stringReader);

var geometry = serializer.Deserialize<Geometry>(jsonReader);

and

var geometry = new Point(0, 0);

var serializer = GeoJsonSerializer.Create();
using var stringWriter = new StringWriter();
using var jsonWriter = new JsonTextWriter(stringWriter);

serializer.Serialize(jsonWriter, geometry);
var geoJson = stringWriter.ToString();

The GeoJsonSerializer.Create() method just creates a new JsonSerializer with the GeoJSON converter:

    public new static JsonSerializer Create()
    {
        return CreateDefault();
    }

    public new static JsonSerializer CreateDefault()
    {
        var s = JsonSerializer.CreateDefault();
        s.NullValueHandling = NullValueHandling.Ignore;

        AddGeoJsonConverters(s, Wgs84Factory, 2, false, null);
        return s;
    }

Creating your own

If you decide to create your own GeoJSON converters, a great place to start would be to check the GeoJSON4STJ converters. There's not just one converter, there's a different converter for each type, or rather, category of types. There are different converters for coordinates, arrays and single features. This makes serializing a lot easier and faster. Once you read the type you know what to do with the other attributes of an object.

Another option is to load the entire JSON string as a JsonDocument and use a Visitor to go over all nodes and parse them according to their type. That's slower and uses more memory as the JsonDocument is essentially a copy of the source data.

Upvotes: 5

Related Questions