Подтвердить что ты не робот

JSON.NET - условная десериализация типа

Я использую некоторые веб-службы ARCGis, и у них есть какой-то неудачный дизайн JSON. например, они могут дать что-то вроде этого:

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

Теперь, в зависимости от переданного значения geometryType, объект геометрии может быть одним из нескольких разных типов объектов. в приведенном выше случае геометрия node имеет тип Polygon.

так, вопрос есть; в JSON.NET есть ли способ обозначить эту условную типизацию? if not (что я сомневаюсь, что есть), есть ли способ создать поставщика для десериализации этой геометрии node на основе информации об объекте выше? если нет, есть ли рекомендуемые способы решения этого вопроса?

edit: я довольно подробно рассмотрел создание пользовательского конвертера, но проблема с конвертером заключается в том, что у них есть этот абстрактный метод:

public override T Create (Type objectType)

однако, я не знаю, какой тип для создания здесь, мне нужно знать, какой объект был указан в JSON выше.

спасибо!

4b9b3361

Ответ 1

Я собрал образец конвертера, чтобы указать вам в правильном направлении. Вот мои примеры строк JSON:

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

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

Я протестировал его так:

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

//Should have correctly typed instance here...

И вот объекты геометрии конвертера и образца:

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; }
}

Ответ 2

У меня была аналогичная проблема и разрешил ее с помощью JsonSchema, благодаря Ювалю Ичакову help: Deserialize json in способ "TryParse"

Похоже на что-то подобное:

    // 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;
    }