继承Dictionary时的序列化
Serialization when inheriting from Dictionary
我正在使用 System.Web.Script.Serialization.JavaScriptSerializer
序列化/反序列化扩展字典的 class。
问题是,我的自定义属性没有被序列化。这是我的 class:
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
。
这可能会导致使用 属性 索引器的 class 中出现强制转换异常(如已接受的答案中所建议),例如:
public class Test : Dictionary<string, dynamic> {
public ushort Id { get => this[nameof(Id)]; set => this[nameof(Id)] = value; }
}
Id
属性 getter 将尝试将 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; }
}
它在序列化之前尝试获取 属性,结果是 'key not found exception'。我不明白为什么它仅在引用类型时才尝试获取 属性,这对我来说似乎是个错误...
所以你必须这样做:
public Test Child { get => this.ContainsKey(index) ? this[nameof(Child)] : null; ... }
只是总结评论:
- MSDN 建议即使在 JavaScriptSerializer 自己的文档中也使用 JSON.NET
- 在这种情况下,组合将允许您使用 JavaScriptSerializer(而不是继承)
- 要使用现有数据结构(继承),您必须实现自己的 JavaScriptObjectDeserializer 版本 (https://referencesource.microsoft.com/#system.web.extensions/Script/Serialization/JavaScriptObjectDeserializer.cs,2f8d1f9fbf43dbfa)
- 默认的序列化器只支持忽略属性的属性(不是include/rename)
要使用组合,您只需像这样修改您的测试对象:
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}}
更新:属性 索引器
您可以将默认索引器添加到您的 class 以便以下代码可以工作:
test["A"] = 1;
var result = test["A"];
这是为默认索引器添加的代码:
public object this[string key]
{
get { return this.Items[key]; }
set { this.Items[key] = value; }
}
我想你可以将其扩展到实现 IDictionary,但我认为只使用合成应该是最简单的。
我正在使用 System.Web.Script.Serialization.JavaScriptSerializer
序列化/反序列化扩展字典的 class。
问题是,我的自定义属性没有被序列化。这是我的 class:
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
。
这可能会导致使用 属性 索引器的 class 中出现强制转换异常(如已接受的答案中所建议),例如:
public class Test : Dictionary<string, dynamic> {
public ushort Id { get => this[nameof(Id)]; set => this[nameof(Id)] = value; }
}
Id
属性 getter 将尝试将 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; }
}
它在序列化之前尝试获取 属性,结果是 'key not found exception'。我不明白为什么它仅在引用类型时才尝试获取 属性,这对我来说似乎是个错误...
所以你必须这样做:
public Test Child { get => this.ContainsKey(index) ? this[nameof(Child)] : null; ... }
只是总结评论:
- MSDN 建议即使在 JavaScriptSerializer 自己的文档中也使用 JSON.NET
- 在这种情况下,组合将允许您使用 JavaScriptSerializer(而不是继承)
- 要使用现有数据结构(继承),您必须实现自己的 JavaScriptObjectDeserializer 版本 (https://referencesource.microsoft.com/#system.web.extensions/Script/Serialization/JavaScriptObjectDeserializer.cs,2f8d1f9fbf43dbfa)
- 默认的序列化器只支持忽略属性的属性(不是include/rename)
要使用组合,您只需像这样修改您的测试对象:
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}}
更新:属性 索引器
您可以将默认索引器添加到您的 class 以便以下代码可以工作:
test["A"] = 1;
var result = test["A"];
这是为默认索引器添加的代码:
public object this[string key]
{
get { return this.Items[key]; }
set { this.Items[key] = value; }
}
我想你可以将其扩展到实现 IDictionary,但我认为只使用合成应该是最简单的。