Десериализация пустого словаря в Json.Net без JSON_FORCE_OBJECT

103
03 сентября 2019, 07:00

Существует сервис на PHP, от которого я получаю json и выполняю при помощи Json.Net парсинг.

Вот так выглядит упрощённо мой запрос:

var result = this.GetTestData1();
var deserialized = JsonConvert.DeserializeObject<Response>(result);
private string GetTestData1()
{
    return @"
        {
          'students': {
           '123': 100,
           '1234': 404,
          }
        }
    ";
}

Где классы для десериализации словаря выглядят так:

public class Response
{
    [JsonProperty("students")]
    public Dictionary<string, StudentResult> StudentsResults { get; set; }
}
public enum StudentResult
{
    Success = 100,
    ContactPersonNotFound = 404,
}

Но есть вот какая проблема. В случае если PHP отправит пустой массив, то я падаю с ошибкой JsonSerializationException:

JsonSerializationException: Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'System.Collections.Generic.Dictionary`2[System.String,UserQuery+StudentResult]' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.

To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array. Path 'students', line 3, position 17.

Проблема в том, что PHP неожиданно отправляет этот json со скобками не фигурными ({}), а квадратными ([]):

private string GetTestData2()
{
    return @"
        {
          'students': []
        }
    ";
}

Точнее, проблема в том, что мопед не мой я не могу заставить на стороне PHP включить JSON_FORCE_OBJECT (см. например тут)

Поэтому ищутся варианты решения, пусть даже и в виде костылей, на стороне Json.Net, а не на стороне PHP.

Сам искал по документации штуки типа NullValueHandling/DefaultValueHandling/ObjectCreationHandling - но похоже, что всё не то. :(

Answer 1

Нашёл ответ тут: How to deserialize object that can be an array or a dictionary with Newtonsoft?

public class Response
{
    [JsonProperty("students")]
    [JsonConverter(typeof(EmptyArrayOrDictionaryConverter))]
    public Dictionary<string, StudentResult> StudentsResults { get; set; }
}
public class EmptyArrayOrDictionaryConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsAssignableFrom(typeof(Dictionary<string, object>));
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        if (token.Type == JTokenType.Object)
        {
            return token.ToObject(objectType);
        }
        else if (token.Type == JTokenType.Array)
        {
            if (!token.HasValues)
            {
                // create empty dictionary
                return Activator.CreateInstance(objectType);
            }
        }
        throw new JsonSerializationException("Object or empty array expected");
    }
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}
READ ALSO
Как правильно вывести html через функцию echo

Как правильно вывести html через функцию echo

В теге use идёт ссылка на svg спрайт и эта ссылка "не работает", то есть не задаёт правильный адрес

88
Убрать ненужные пиксели

Убрать ненужные пиксели

Есть небольшой скрипт, он прогоняет через себя все пиксели некоторого изображения, сохраняет в ассоциативный массив цвет пикселя и количество...

109
Исключение на не известную функцию в С++

Исключение на не известную функцию в С++

Подскажите, совсем новичкуНужно сделать исключение вызову функции, которое не поддерживается операционной системой

115