NetTopologySuite / NetTopologySuite.IO.GeoJSON

GeoJSON IO module for NTS.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

NetTopologySuite.IO.GeoJSON4STJ: Serialize IAttributeTable with array of dictionary data

atlefren opened this issue · comments

I have come across a case where NetTopologySuite.IO.GeoJSON4STJ does something strange to my FeatureCollections, where some properties comes out as lists of empty lists.

I've narrowed this down to four test-cases showcasingf the behaviour, see below.

However, first a bit of context:

GeoJSON4STJ is a relatively new addition to an api project which basically queries a PostGIS database and returns a featureCollection as json.

Data in the database is stored as

geom geometry
properties jsonb

Existing code for reading this reads the properties column as a string, and then hands it to a AttributesHelper-class, which basically does this:

public static IAttributesTable ConvertAttributes(string data)
            => JsonConvert.DeserializeObject<Dictionary<string, object>>(data).Aggregate(new AttributesTable(), Add);
 private static AttributesTable Add(AttributesTable acc, KeyValuePair<string, object> kv)
        {
            if (kv.Key != null && !acc.Exists(kv.Key))
            {
                acc.Add(kv.Key, kv.Value);
            }
            
            return acc;
        }

I.e, it is using Newtonsoft to read the json-string retrieved from postgres as a Dictionary<string,object> and then adds this to a Feature, and later a FeatureCollection, which at some point is returned as a controller, where GeoJSON4STJ applies its serialization.

This works fine, except for cases where the properties json is compex, like this:

"{\"testListWithDicts\": [{\"a\": 1}]}"

The result when using GeoJSON4STJ is this

"{"testListWithDicts":[[[[]]]]}"

However, if I use GeoJsonWriter, I get:

"{"testListWithDicts":[{"a":1}]}"

Any ideas, pointers or tips greatly appreciated, I would really like to continue using GeoJsonSTJ.

I am aware that I probably should use something other that Newtonsoft to parse the json string in the beginning, but I have no idea what I can do here and still make things work?

Complete tests showing this issue

using System.Text.Json;
using DatavarehusApiImplementation.Util;
using NetTopologySuite.Features;
using NetTopologySuite.IO;
using NetTopologySuite.IO.Converters;
using NUnit.Framework;

namespace My.Tests
{
    [TestFixture]
    public class AttributesHelperTest
    {
        [Test]
        public void TestPlainJson()
        {
            var attribs = "{\"testString\":\"string\", \"testInt\":1}";
            var properties = AttributesHelper.ConvertAttributes(attribs);
            var c = ConvertStj(properties);
            var expected = ConvertGeoJsonWriter(properties);
            Assert.AreEqual(expected, c);
        }

        [Test]
        public void TestJsonListInAttribs()
        {
            var attribs = "{\"testString\":\"string\", \"testInt\":1 ,\"testList\": [1,2,3] }";
            var properties = AttributesHelper.ConvertAttributes(attribs);
            var c = ConvertStj(properties);
            var expected = ConvertGeoJsonWriter(properties);
            Assert.AreEqual(expected, c);
        }

        [Test]
        public void TestJsonDictionaryInAttribs()
        {
            var attribs = "{\"testDict\": {\"a\": 1} }";
            var properties = AttributesHelper.ConvertAttributes(attribs);
            var c = ConvertStj(properties);
            var expected = ConvertGeoJsonWriter(properties);
            Assert.AreEqual(expected, c);
        }

        [Test]
        public void TestJsonListOfDictionariesInAttribs()
        {
            var attribs = "{\"testListWithDicts\": [{\"a\": 1}]}";
            var properties = AttributesHelper.ConvertAttributes(attribs);
            var c = ConvertStj(properties);
            var expected = ConvertGeoJsonWriter(properties);
            Assert.AreEqual(expected, c);
        }

        private static string ConvertGeoJsonWriter(IAttributesTable attrs)
            => new GeoJsonWriter().Write(attrs);


        private static string ConvertStj(IAttributesTable attrs)
        {
            var geoJsonConverterFactory = new GeoJsonConverterFactory();
            var serializerOptions = new JsonSerializerOptions
            {
                Converters = { geoJsonConverterFactory }
            };
            var str = JsonSerializer.Serialize(attrs, serializerOptions);
            return str;
        }
    }
}

Follow-up: Found this blog post on reading a Dictionary<string, object> using STJ: https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/

When replacing the existing newtonsoft-code with this, everything works as expected.

I guess the mix of Newtonsoft and STJ caused the problems here.

@atlefren, can this be closed?