序列化和反序列化不适用于字典作为支持字段
Serialization & Deserialization wont work with Dictionary as Backing field
当字典隐藏在 属性 后面的支持字段中时,我 运行 遇到了一个奇怪的问题 Serialization/Deserialization 字典。
这是 Fiddle:
https://dotnetfiddle.net/RFZEur
最终结果:
- 语言:C#
- 框架:.Net Framework 4.8
- 有一个私有支持字段,它具有与
的配对组合
- 可以序列化为字符串列表(不包含整数,指向引用的支持字段)
- 不能从序列化列表中反序列化 -- 支持字典不会被填充。
public class SanityChecks
{
private readonly ITestOutputHelper _testOutputHelper;
public SanityChecks(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
public class TestClass
{
[JsonIgnore]
public Dictionary<int,string> _prvList = new Dictionary<int, string>();
public IEnumerable<string> ListValues
{
get => _prvList.Select(p=> p.Value).ToList();
set
{
var valArr = value.ToArray();
for (var x = 0; x < valArr.Length; x++)
{
_prvList.Add(x,valArr[x]);
}
}
}
}
[Fact]
public void SanityCheck_CanDeserialize()
{
var assumption = "{\"ListValues\":[\"TestValue\",\"AAA\"]}";
var actual = JsonConvert.DeserializeObject<TestClass>(assumption);
Assert.Equal(2, actual._prvList.Count());
Assert.Equal(2, actual.ListValues.Count());
}
[Fact]
public void SanityCheck_CanSerialize()
{
var assumption = new TestClass() { ListValues = new[] { "TestValue", "AAA" } };
var actualSerialized = JsonConvert.SerializeObject(assumption);
_testOutputHelper.WriteLine(actualSerialized);
Assert.Equal("{\"ListValues\":[\"TestValue\",\"AAA\"]}", actualSerialized);
}
[Fact]
public void SanityCheck_CanDeserializeFromSerialized()
{
var assumption = new TestClass() { ListValues = new[] { "TestValue", "AAA" } };
var actualSerialized = JsonConvert.SerializeObject(assumption);
_testOutputHelper.WriteLine(actualSerialized);
var actualDeserialized = JsonConvert.DeserializeObject<TestClass>(actualSerialized);
Assert.Equal(2, actualDeserialized._prvList.Count());
var actualDeserializedSerialized = JsonConvert.SerializeObject(actualDeserialized);
_testOutputHelper.WriteLine(actualDeserializedSerialized);
Assert.Equal(actualSerialized, actualDeserializedSerialized);
}
}
如果您对如何检索此结果有一些建议,我愿意。我正在使用 XUnit 进行测试,但是 fiddle 可以快速执行下面的测试,并稍作修改使其成为控制台应用程序。
我已经尝试将 ISerializable
的实现应用到对象中,但是我 运行 遇到了同样的问题。
值得注意的奇怪之处:IEnumerable
中 Get 子句的 删除 导致反序列化工作(序列化不再工作)
编辑:
为了更加清楚,我需要将 int、string 对的映射序列化为字符串列表,并且我需要将相同的序列化版本反序列化为 int、string 对的集合。
对于Fiddle:应该没有异常抛出
对于 XUnit:所有测试都应该通过。
反序列化后ListValues
枚举为空的原因有以下几个因素:
- 默认情况下,Json.Net 将在反序列化过程中重用现有对象值,而不是创建新值。因此,对于
ListValues
之类的属性,它将首先调用访问器,然后找到一个现有列表,然后从 JSON. 继续填充它
ListValues
的访问器并不是每次都return 相同的列表实例;它总是从 _prvList
支持字段创建一个新实例。
因此,这是反序列化过程中发生的事情:
- 序列化程序调用
ListValues
访问器,它 return 是从字典创建的新空列表。
- 序列化程序从 JSON.
填充该列表
- 已填充的列表被丢弃(序列化程序假定
TestClass
实例已经引用了该列表,因此它永远不会调用修改器)。
- 当测试稍后调用
ListValues
访问器时,它 return 再次从字典创建一个新的空列表。
您可以通过将 ObjectCreationHandling
设置为 Replace
来更改序列化程序的行为。在反序列化期间应用此设置可以让您的测试通过。 Fiddle here.
当字典隐藏在 属性 后面的支持字段中时,我 运行 遇到了一个奇怪的问题 Serialization/Deserialization 字典。
这是 Fiddle: https://dotnetfiddle.net/RFZEur
最终结果:
- 语言:C#
- 框架:.Net Framework 4.8
- 有一个私有支持字段,它具有与
- 可以序列化为字符串列表(不包含整数,指向引用的支持字段)
- 不能从序列化列表中反序列化 -- 支持字典不会被填充。
public class SanityChecks
{
private readonly ITestOutputHelper _testOutputHelper;
public SanityChecks(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
public class TestClass
{
[JsonIgnore]
public Dictionary<int,string> _prvList = new Dictionary<int, string>();
public IEnumerable<string> ListValues
{
get => _prvList.Select(p=> p.Value).ToList();
set
{
var valArr = value.ToArray();
for (var x = 0; x < valArr.Length; x++)
{
_prvList.Add(x,valArr[x]);
}
}
}
}
[Fact]
public void SanityCheck_CanDeserialize()
{
var assumption = "{\"ListValues\":[\"TestValue\",\"AAA\"]}";
var actual = JsonConvert.DeserializeObject<TestClass>(assumption);
Assert.Equal(2, actual._prvList.Count());
Assert.Equal(2, actual.ListValues.Count());
}
[Fact]
public void SanityCheck_CanSerialize()
{
var assumption = new TestClass() { ListValues = new[] { "TestValue", "AAA" } };
var actualSerialized = JsonConvert.SerializeObject(assumption);
_testOutputHelper.WriteLine(actualSerialized);
Assert.Equal("{\"ListValues\":[\"TestValue\",\"AAA\"]}", actualSerialized);
}
[Fact]
public void SanityCheck_CanDeserializeFromSerialized()
{
var assumption = new TestClass() { ListValues = new[] { "TestValue", "AAA" } };
var actualSerialized = JsonConvert.SerializeObject(assumption);
_testOutputHelper.WriteLine(actualSerialized);
var actualDeserialized = JsonConvert.DeserializeObject<TestClass>(actualSerialized);
Assert.Equal(2, actualDeserialized._prvList.Count());
var actualDeserializedSerialized = JsonConvert.SerializeObject(actualDeserialized);
_testOutputHelper.WriteLine(actualDeserializedSerialized);
Assert.Equal(actualSerialized, actualDeserializedSerialized);
}
}
如果您对如何检索此结果有一些建议,我愿意。我正在使用 XUnit 进行测试,但是 fiddle 可以快速执行下面的测试,并稍作修改使其成为控制台应用程序。
我已经尝试将 ISerializable
的实现应用到对象中,但是我 运行 遇到了同样的问题。
值得注意的奇怪之处:IEnumerable
中 Get 子句的 删除 导致反序列化工作(序列化不再工作)
编辑: 为了更加清楚,我需要将 int、string 对的映射序列化为字符串列表,并且我需要将相同的序列化版本反序列化为 int、string 对的集合。
对于Fiddle:应该没有异常抛出 对于 XUnit:所有测试都应该通过。
反序列化后ListValues
枚举为空的原因有以下几个因素:
- 默认情况下,Json.Net 将在反序列化过程中重用现有对象值,而不是创建新值。因此,对于
ListValues
之类的属性,它将首先调用访问器,然后找到一个现有列表,然后从 JSON. 继续填充它
ListValues
的访问器并不是每次都return 相同的列表实例;它总是从_prvList
支持字段创建一个新实例。
因此,这是反序列化过程中发生的事情:
- 序列化程序调用
ListValues
访问器,它 return 是从字典创建的新空列表。 - 序列化程序从 JSON. 填充该列表
- 已填充的列表被丢弃(序列化程序假定
TestClass
实例已经引用了该列表,因此它永远不会调用修改器)。 - 当测试稍后调用
ListValues
访问器时,它 return 再次从字典创建一个新的空列表。
您可以通过将 ObjectCreationHandling
设置为 Replace
来更改序列化程序的行为。在反序列化期间应用此设置可以让您的测试通过。 Fiddle here.