Сериализация при наследовании из словаря - PullRequest
0 голосов
/ 18 октября 2018

Я использую System.Web.Script.Serialization.JavaScriptSerializer для сериализации / десериализации класса, расширяющего словарь.

Проблема в том, что мои пользовательские свойства не сериализуются.Вот мой класс:

public class Test : Dictionary<string, object> {
    public ushort Id { get; set; }
    public string Name { get; set; }
}

И мой код:

var jss = new JavaScriptSerializer();

var test = new Test {
    Id = 123,
    Name = "test"
};

var json = jss.Serialize(test);

Результат в json это пустой JSON {}

Я не хочузависеть от Newtonsoft или JSON.Net или любой другой библиотеки.

ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ

Я только что заметил некоторые, хм, особенности при использовании как dynamic, так и object:

  • JavaScriptSerializer по умолчанию любое числовое значение равно int.

  • Кроме того, Newtonsoft по умолчанию любое число long.

Это может привести к исключениям приведения в классе с использованием индексатора свойств (как предлагается в принятом ответе), например:

public class Test : Dictionary<string, dynamic> {
    public ushort Id { get => this[nameof(Id)]; set => this[nameof(Id)] = value; }
}

Idgetter попытается неявно преобразовать int в ushort, что приведет к сбою.

ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ 2

Я только что обнаружил так много странного поведения с Newtonsoft:

Я добавил эти атрибуты для решения проблемы 'long to ushort':

[JsonObject(MemberSerialization.OptIn)]
public class Test : Dictionary<string, dynamic> {
    [JsonProperty]
    public ushort Id { get => this[nameof(Id)]; set => this[nameof(Id)] = value; }
}

Вышеприведенное работает!Но когда свойство является ссылочным типом:

[JsonObject(MemberSerialization.OptIn)]
public class Test : Dictionary<string, dynamic> {
    [JsonProperty]
    public ushort Id { get => this[nameof(Id)]; set => this[nameof(Id)] = value; }
    [JsonProperty]
    public Test Child { get => this[nameof(Child)]; set => this[nameof(Child)] = value; }
}

Оно пытается получить свойство перед его сериализацией, что приводит к «исключению ключа не найден».Я не понимаю, почему он пытается получить свойство только тогда, когда он является ссылочным типом, для меня это похоже на ошибку ...

Так что вы должны сделать что-то вроде этого:

public Test Child { get => this.ContainsKey(index) ? this[nameof(Child)] : null; ... }

1 Ответ

0 голосов
/ 18 октября 2018

Просто суммируем комментарии:

  • MSDN рекомендует использовать JSON.NET даже на собственных документах JavaScriptSerializer
  • Композиция позволит вам использовать JavaScriptSerializer в этом случае (вместо наследования)
  • Для работы с существующей структурой данных (наследование) вам потребуется реализовать собственную версию JavaScriptObjectDeserializer (https://referencesource.microsoft.com/#system.web.extensions/Script/Serialization/JavaScriptObjectDeserializer.cs,2f8d1f9fbf43dbfa)
  • Сериализатор по умолчанию поддерживает только атрибут, который игнорирует атрибут (не включает / не переименовывает))

Чтобы использовать композицию, вам просто нужно изменить свой тестовый объект следующим образом:

public class Test
{
    public ushort Id { get; set; }
    public string Name { get; set; }
    public Dictionary<string, object> Items { get; set; } = new Dictionary<string, object> {};
}

Тогда следующий код будет работать нормально:

var jss = new JavaScriptSerializer();
var test = new Test
{
    Id = 123,
    Name = "test",
};
test.Items.Add("A", 1);
var json = jss.Serialize(test);

Вывод будет просто:

{"Id": 123, "Name": "test", "Items": {"A": 1}}

ОБНОВЛЕНИЕ: Индексатор свойств

Вы можете добавить индексатор по умолчанию для вашего класса, чтобы работал следующий код:

test["A"] = 1;
var result = test["A"];

Вот код длядобавить для индексатора по умолчанию:

public object this[string key]
{
    get { return this.Items[key]; }
    set { this.Items[key] = value; }
}

Вы можете расширитьэто в реализации IDictionary я полагаю, но я думаю, что просто работать с композицией должно быть проще всего.

...