24

I'm consuming some ARCGis web services, and they have some unfortunate JSON design. for example, they might give something like this:

{
geometryType: "esriGeometryPolygon"
geometry: {
-rings: [
-[.blah..... }}

Now, depending on the geometryType value passed in, the geometry object may be one of several different object types. in the case above, the geometry node is of type Polygon.

so, question is; in JSON.NET is there any way to notate this conditional typing? if not (which i doubt there is), is there a way to build a provider for deserializing that geometry node, based on the object info above? if not, are there any recommended ways for solving this?

edit: i looked pretty extensively into building a custom converter, but the problem with the converter is that they have this abstract method:

public override T Create (Type objectType)

however, i have no way of knowing what type to create here, i need to know what kind of object was specified in the JSON above.

thanks!

bryan costanich
  • 1,719
  • 6
  • 20
  • 33

2 Answers2

13

I put together a sample converter to point you in the right direction. Here are my sample JSON strings:

{geometryType: "esriGeometryPolygon", geometry: { rings: 5 } }

{geometryType: "esriGeometryOther", geometry: { rings: 5 } }

I tested it like this:

var serializer = new JsonSerializer();
var geometry = serializer.Deserialize<Geometry>(new JsonTextReader(new StringReader(jsonData)));

//Should have correctly typed instance here...

And here is the converter and sample geometry objects:

public class GeometryConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;

        reader.Read(); // startobject

        //we should be at geometry type property now
        if ((string)reader.Value != "geometryType") throw new InvalidOperationException();

        reader.Read(); //propertyName

        var type = (string)reader.Value;

        Geometry value;

        switch(type)
        {
            case "esriGeometryPolygon":
                value = new PolygonGeometry();
                break;
            case "esriGeometryOther":
                value = new OtherGeometry();
                break;
            default:
                throw new NotSupportedException();
        }

        reader.Read(); // move to inner object property
        //should probably confirm name here

        reader.Read(); //move to inner object

        serializer.Populate(reader, value);

        reader.Read(); //move outside container (should be end object)

        return value;
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(Geometry).IsAssignableFrom(objectType);
    }
}

[JsonConverter(typeof(GeometryConverter))]
public class OtherGeometry : Geometry
{

}

[JsonConverter(typeof(GeometryConverter))]
public class PolygonGeometry : Geometry
{

}

[JsonConverter(typeof(GeometryConverter))]
public class Geometry
{
    public int rings { get; set; }
}
Community
  • 1
  • 1
Paul Tyng
  • 7,924
  • 1
  • 33
  • 57
  • 1
    JSON is unordered. There is no guarantee that the type will be the first property, or even that it will be before the child object – r_c May 13 '21 at 01:27
  • 1.) Same question as above: how do you know the order of the properties. 2.) How does this work if there are properties at the same level as 'geometryType' that need to be deserialized, like 'tag' or 'name'. 3.) How does this work if there are properties inside the 'geometry' that need to be deseralized? – Quark Soup Apr 02 '23 at 14:04
2

I had a similar problem and resolved it with a JsonSchema, thanks to Yuval Itzchakov's help : Deserialize json in a “TryParse” way

It looks like something like that :

    // Check json schema :
    const string errorJsonSchema =
        @"{
              'type': 'object',
              'properties': {
                  'error': {'type':'object'},
                  'status': {'type': 'string'},
                  'code': {'type': 'string'}
              },
              'additionalProperties': false
          }";
    JsonSchema schema = JsonSchema.Parse(errorJsonSchema);
    JObject jsonObject = JObject.Parse(jsonResponse);
    if (!jsonObject.IsValid(schema))
    {
        error = null;
        return false;
    }

    // Try to deserialize :
    try
    {
        error = new JsonSerializer<Error>.DeserializeFromString(jsonResponse);
        return true;
    }
    catch
    {
        // The JSON response seemed to be an error, but failed to deserialize.
        // This case should not occur...
        error = null;
        return false;
    }
Community
  • 1
  • 1
Dude Pascalou
  • 2,989
  • 4
  • 29
  • 34