当您启用数据协定序列化程序的 object reference preservation 功能时,可能会抛出异常消息 Deserialized object with reference id 'i53' not found in stream。表示在反序列化过程中遇到了对未定义对象的引用,因此无法反序列化。
我能够通过如下废弃数据成员来重现该问题。首先,我定义了以下类型:
namespace V1
{
[DataContract(Name = "Member", Namespace = "Question45008433", IsReference = true)]
public class Member
{
[DataMember]
public string Name { get; set; }
}
[DataContract(Name = "Root", Namespace = "Question45008433")]
public class RootObject
{
[DataMember(Order = 1)]
public Member MainMember { get; set; }
[DataMember(Order = 2)]
public List<Member> Members { get; set; }
}
}
然后我创建了一个测试对象如下:
var list = new List<V1.Member> { new V1.Member { Name = "Foo" }, new V1.Member { Name = "Bar" } };
var v1 = new V1.RootObject { MainMember = list[0], Members = list };
注意Foo 对象被引用了两次,一次来自MainMember,一次来自Members 列表。
当我使用DataContractSerializer 对其进行序列化时,我得到了以下 XML:
<Root xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="Question45008433">
<MainMember z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<Name>Foo</Name>
</MainMember>
<Members>
<Member z:Ref="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" />
<Member z:Id="i2" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<Name>Bar</Name>
</Member>
</Members>
</Root>
注意Foo 对象在第一次序列化为<MainMember> 时被完全序列化,因此它被赋予z:Id="i1" 属性。序列化过程中遇到后续引用时,仅通过z:Ref="i1"序列化一个引用。
接下来,我认为 MainMember 数据成员是不必要的并已废弃:
namespace V2
{
[DataContract(Name = "Member", Namespace = "Question45008433", IsReference = true)]
public class Member
{
[DataMember]
public string Name { get; set; }
}
[DataContract(Name = "Root", Namespace = "Question45008433")]
public class RootObject
{
[DataMember(Order = 2)]
public List<Member> Members { get; set; }
}
}
现在,如果我尝试使用此修改后的合约反序列化原始 XML,我会得到您所看到的异常:
System.Runtime.Serialization.SerializationException: Deserialized object with reference id 'i1' not found in stream.
at System.Runtime.Serialization.XmlObjectSerializerReadContext.GetExistingObject(String id, Type type, String name, String ns)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.TryHandleNullOrRef(XmlReaderDelegator reader, Type declaredType, String name, String ns, Object& retObj)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, DataContract& dataContract)
为什么会这样?发生这种情况是因为过时的数据成员在其余数据成员之前出现。因此,在反序列化过程中,定义元素被跳过并忽略,后续引用无法解析。
解决方法是将原始数据成员添加为私有假合成属性,该属性不执行任何操作并始终返回null:
namespace V3
{
[DataContract(Name = "Member", Namespace = "Question45008433", IsReference = true)]
public class Member
{
[DataMember]
public string Name { get; set; }
}
[DataContract(Name = "Root", Namespace = "Question45008433")]
public class RootObject
{
[DataMember(EmitDefaultValue = false, Order = 1)]
Member MainMember
{
get
{
return null;
}
set
{
// Do nothing
}
}
[DataMember(Order = 2)]
public List<Member> Members { get; set; }
}
}
现在可以成功反序列化原始 XML,因为在反序列化期间,数据协定序列化程序本身会按名称维护所有引用元素的查找表。但是,z:Ref="i1" 元素只有在遇到当前有效成员时才会被添加。并且,因为EmitDefaultValue = false,序列化的时候不再出现被废弃的元素。