After scouring SO and Google for answers I'm at a loss, so hoping someone can help me on this one!
I have a project that consumes data from a 3rd party API. The data is a JSON message containing asset locations (lat and lng).
But the API can support polygons and points (shown respectively below):
{
"geom": {
"coordinates": [
[
[
0.07666021585464479,
51.49331798632394
],
[
0.07707864046096803,
51.49337476490021
],
[
0.07717519998550416,
51.49315433003204
],
[
0.07676750421524049,
51.49309087131179
],
[
0.07666021585464479,
51.49331798632394
]
]
],
"type": "Polygon"
}
}
And a point looks like this:
{
"geom": {
"coordinates": [
-0.00203667,
51.51020028
],
"type": "Point"
}
}
I've tried to mock this up and deserialise it with the following:
public class Item
{
[JsonProperty("geom")]
public Geom Geom { get; set; }
}
public class Geom
{
[JsonProperty("coordinates")]
public double[] Coords { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
}
And calling it with:
class Program
{
static void Main(string[] args)
{
new Program();
}
public Program()
{
var single = "{\"geom\":{\"coordinates\":[-0.00203667,51.51020028],\"type\":\"Point\"}}";
var s = JsonConvert.DeserializeObject<Item>(single);
var poly = "{\"geom\":{\"coordinates\":[[[0.07666021585464479,51.49331798632394],[0.07707864046096803,51.49337476490021],[0.07717519998550416,51.49315433003204],[0.07676750421524049,51.49309087131179],[0.07666021585464479,51.49331798632394]]],\"type\":\"Polygon\"}}";
var p = JsonConvert.DeserializeObject<Item>(poly);
}
}
I understand the problem well enough, the poly string needs to be a double[][]
. But I can't seem to get the same deserialisation process to work for both strings.
It works if I deserialise them to an object
, but then I can't use it in my concrete classes without casting everywhere.
I've also seen some attempts on wrapping the 'point' deserialisation in a try/catch and using the catch to deserialise a 'poly' but this seems like a hack and there are potentially other types of coordinate systems in the API.
I'm using Newtonsoft.Json and I've looked into all different approaches, the closest I've gotten is to have different varients of the classes, but as this is a sub class to a larger asset class I don't want to have to create variants all the way up the to top level.
I've also looked at custom deserialisation, but I don't understand it well enough to resolve this.
Also happy to change serialiser if someone thinks this can be solved with another library.
The structure for your data is something like :
public class Item
{
[JsonProperty("geom")]
public Geom Geom { get; set; }
}
public abstract class Geom
{
[JsonProperty("type")]
public string Type { get; set; }
}
public class Polygon : Geom
{
[JsonProperty("coordinates")]
public double[][][] Coords { get; set; }
}
public class Point : Geom
{
[JsonProperty("coordinates")]
public double[] Coords { get; set; }
}
But Json.NET is not able to apply the polymorphism on the deserialization. To do it you'll have to create a custom JsonConverter :
public class GeomConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Geom).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jobject = JObject.Load(reader);
string type = jobject["type"].ToString();
Geom geom = null;
if (type == "Polygon")
{
geom = new Polygon();
}
else if (type == "Point")
{
geom = new Point();
}
serializer.Populate(jobject.CreateReader(), geom);
return geom;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And then use your custom JsonConverter as parameter of the DeserializeObject method:
public Program()
{
var single = "{\"geom\":{\"coordinates\":[-0.00203667,51.51020028],\"type\":\"Point\"}}";
var s = JsonConvert.DeserializeObject<Item>(single, new GeomConverter());
var poly = "{\"geom\":{\"coordinates\":[[[0.07666021585464479,51.49331798632394],[0.07707864046096803,51.49337476490021],[0.07717519998550416,51.49315433003204],[0.07676750421524049,51.49309087131179],[0.07666021585464479,51.49331798632394]]],\"type\":\"Polygon\"}}";
var p = JsonConvert.DeserializeObject<Item>(poly, new GeomConverter());
}