Разрешение типа на основе свойств в JSON.NET

Можно ли переопределить разрешение типа с помощью JSON.NET на основе свойства объекта JSON? На основе существующих API-интерфейсов, похоже, мне нужен способ принять JsonPropertyCollection и вернуть созданный Type.

ПРИМЕЧАНИЕ. Я знаю атрибут TypeNameHandling, но это добавляет свойство $type. Я не контролирую исходный JSON.

Ответы

Ответ 1

Похоже, что это обрабатывается путем создания пользовательского JsonConverter и добавления его к JsonSerializerSettings.Converters перед десериализации.

nonplus оставил удобный образец на JSON.NET дискуссионный форум на Codeplex. Я изменил образец, чтобы вернуть пользовательский Type и отложить до механизма создания по умолчанию, а не создавать экземпляр объекта на месте.

abstract class JsonCreationConverter<T> : JsonConverter
{
    /// <summary>
    /// Create an instance of objectType, based properties in the JSON object
    /// </summary>
    protected abstract Type GetType(Type objectType, JObject jObject);

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

    public override object ReadJson(JsonReader reader, Type objectType, 
        object existingValue, JsonSerializer serializer)
    {
        JObject jObject = JObject.Load(reader);

        Type targetType = GetType(objectType, jObject);

        // TODO: Change this to the Json.Net-built-in way of creating instances
        object target = Activator.CreateInstance(targetType);

        serializer.Populate(jObject.CreateReader(), target);

        return target;
    }
}

И вот пример использования (также обновленный, как указано выше):

class VehicleConverter : JsonCreationConverter<Vehicle>
{
    protected override Type GetType(Type objectType, JObject jObject)
    {
        var type = (string)jObject.Property("Type");
        switch (type)
        {
            case "Car":
                return typeof(Car);
            case "Bike":
                return typeof(Bike);
        }

        throw new ApplicationException(String.Format(
            "The given vehicle type {0} is not supported!", type));
    }
}