Search code examples
json.netnettopologysuite

How do I configure a custom converter that returns json, instead of a string, in NewtonSoft.Json?


I have a simple model that contains a NetTopologySuite Polygon:

public class MyModel
{
    [Newtonsoft.Json.JsonConverter(typeof(MyPolygonConverter))]
    public NetTopologySuite.Geometries.Polygon poly { get; set; }
}

And I've build a custom converter, using NetTopologySuite.IO.GeoJson:

public class MyPolygonConverter : JsonConverter<Polygon>
{
    public override void WriteJson(JsonWriter writer, Polygon value, JsonSerializer serializer)
    {
        var geoJsonSerializer = NetTopologySuite.IO.GeoJsonSerializer.Create();

        using var stringWriter = new StringWriter();
        using var jsonWriter = new JsonTextWriter(stringWriter);

        geoJsonSerializer.Serialize(jsonWriter, value);
        var geoJson = stringWriter.ToString();
        writer.WriteValue(geoJson);
    }
    ...
}

And then I create a model object and serialize it:

var model = new MyModel
{
    poly = new Polygon(new LinearRing(new[]
    {
        new Coordinate(-100, 45),
        new Coordinate(-98, 45),
        new Coordinate(-99, 46),
        new Coordinate(-100, 45),
    }))
};

var geoJson = JsonConvert.SerializeObject(model, Formatting.None, 
    new JsonSerializerSettings
    {
        ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
        NullValueHandling = NullValueHandling.Ignore
    });

And what I get contains the Polygon as an escaped GeoJson string:

{
    "poly" : "{\"type\":\"Polygon\",\"coordinates\":[[[-100.0,45.0],[-98.0,45.0],[-99.0,46.0],[-100.0,45.0]]]}"
}

What I want is the Polygon as json, in the json object:

{
    "poly" : {
        "type" : "Polygon",
        "coordinates" : [
            [
                [-100.0,45.0],
                [-98.0,45.0],
                [-99.0,46.0],
                [-100.0,45.0]
            ]
        ]
    }
}

What I'm thinking is that I shouldn't be using a JsonConverter at all, but something else.

But what?


Solution

  • You can use JsonWriter.WriteRawValue(string) to write a raw JSON value directly to the JSON output:

    writer.WriteRawValue(geoJson);
    

    That being said, GeoJsonSerializer is a subclass of Newtonsoft's own JsonSerializer so you can just pass the incoming JsonWriter in to its Serialize() method:

    public override void WriteJson(JsonWriter writer, Polygon value, JsonSerializer serializer)
    {
        var geoJsonSerializer = NetTopologySuite.IO.GeoJsonSerializer.Create();
        geoJsonSerializer.Serialize(writer, value);
    }
    

    Demo fiddle #1 here.

    The only difference between GeoJsonSerializer and the base class is that NullValueHandling.Ignore is enabled by default and several converters are added, so you could also just use this serializer to serialize your entire model, thereby eliminating the need to add [Newtonsoft.Json.JsonConverter(typeof(MyPolygonConverter))] to your model:

    var serializer = NetTopologySuite.IO.GeoJsonSerializer.CreateDefault(
        new JsonSerializerSettings
        {
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            NullValueHandling = NullValueHandling.Ignore,
            Formatting = Formatting.Indented, // Or none, if you prefer
        });
    var sb = new StringBuilder();
    using (var sw = new StringWriter(sb))
    using (var jsonWriter = new JsonTextWriter(sw))
        serializer.Serialize(jsonWriter, model);
    
    var geoJson = sb.ToString();
    

    Demo fiddle #2 here.