使用 DataContractSerializer 反序列化 XML 时出错
Error deserializing XML with DataContractSerializer
我正在使用此方法将 xml 反序列化为对象:
public T Deserialize(string filename)
{
var xml = File.ReadAllText(filename);
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(xml);
writer.Flush();
stream.Position = 0;
DataContractSerializer dcs = new DataContractSerializer(typeof(T));
T obj = (T)dcs.ReadObject(stream);
return obj;
}
我有一个旧的 XML,从那以后一些属性 added/removed 来自 class 我是 serializing/deserializing。
我有以下异常:
Deserialized object with reference id 'i53' not found in stream.
是否可以将 DataContractSerializer 自定义为仅跳过 属性(如果它不再存在于模型中)?请注意,删除的 属性 是对另一个复杂对象的引用,而不是对简单类型的引用。 XML 文件包含那个,我的 class 没有了。
当您启用数据协定序列化程序的 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)
为什么会这样?发生这种情况是因为废弃的数据成员 在 其余数据成员之前出现。因此,在反序列化过程中,定义元素被跳过和忽略,后续引用无法解析。
解决方法是将原始数据成员添加回作为私人伪合成 属性,它什么都不做并且总是 returns 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 现在可以成功反序列化,因为在反序列化过程中,数据协定序列化程序本身会按名称维护对所有引用元素的查找 table。但是,只有当 z:Ref="i1"
元素对应于当前有效的成员时才会添加该元素。并且,因为EmitDefaultValue = false
,被废弃的元素在序列化时将不再出现
我正在使用此方法将 xml 反序列化为对象:
public T Deserialize(string filename)
{
var xml = File.ReadAllText(filename);
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(xml);
writer.Flush();
stream.Position = 0;
DataContractSerializer dcs = new DataContractSerializer(typeof(T));
T obj = (T)dcs.ReadObject(stream);
return obj;
}
我有一个旧的 XML,从那以后一些属性 added/removed 来自 class 我是 serializing/deserializing。
我有以下异常:
Deserialized object with reference id 'i53' not found in stream.
是否可以将 DataContractSerializer 自定义为仅跳过 属性(如果它不再存在于模型中)?请注意,删除的 属性 是对另一个复杂对象的引用,而不是对简单类型的引用。 XML 文件包含那个,我的 class 没有了。
当您启用数据协定序列化程序的 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)
为什么会这样?发生这种情况是因为废弃的数据成员 在 其余数据成员之前出现。因此,在反序列化过程中,定义元素被跳过和忽略,后续引用无法解析。
解决方法是将原始数据成员添加回作为私人伪合成 属性,它什么都不做并且总是 returns 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 现在可以成功反序列化,因为在反序列化过程中,数据协定序列化程序本身会按名称维护对所有引用元素的查找 table。但是,只有当 z:Ref="i1"
元素对应于当前有效的成员时才会添加该元素。并且,因为EmitDefaultValue = false
,被废弃的元素在序列化时将不再出现