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?