我使用此方法将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,从那时起我在序列化/反序列化的类中添加/删除了一些属性。
我有以下例外:
反序列化的对象,引用ID为' i53'在溪流中找不到。
是否可以自定义DataContractSerializer,只是跳过属性,如果它不在模型中?请注意,deleted属性是对另一个复杂对象的引用,而不是对简单类型的引用。 XML文件包含了我的课程,不再是我的课程了。
答案 0 :(得分:1)
启用数据协定序列化程序的object reference preservation功能时,可能会抛出异常消息func readJson() {
do {
if let file = Bundle.main.url(forResource: "LinqContents", withExtension: "json") {
let data = try Data(contentsOf: file)
let json = try JSONSerialization.jsonObject(with: data, options: [])
if let object = json as? [String: Any] {
// json is a dictionary
print(object)
} else if let object = json as? [Any] {
// json is an array
print(object)
} else {
print("JSON is invalid")
}
} else {
print("no file")
}
} catch {
print(error.localizedDescription)
}
}
。它表明,在反序列化期间,遇到了对未定义对象的引用,因此无法反序列化。
我能够通过废弃数据成员来重现问题,如下所示。首先,我定义了以下类型:
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
对其进行序列化时,我得到了以下XML:
DataContractSerializer
请注意,<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
现在,如果我尝试使用此修改后的合同反序列化原始XML,我会看到您看到的异常:
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; }
}
}
为什么会这样?之所以出现这种情况,是因为已废弃的数据成员>>之前 其余数据成员。因此,在反序列化期间,定义元素被跳过并忽略,并且后续引用无法得到解析。
解决方法是将原始数据成员添加回私有伪合成属性,该属性不执行任何操作并始终返回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
现在可以成功反序列化原始XML,因为在反序列化期间,数据协定序列化程序本身会按名称维护所有引用元素的查找表。但是,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; }
}
}
元素仅在遇到时才会添加,如果它对应于当前有效的成员。而且,因为z:Ref="i1"
,序列化后将不再显示已废弃的元素。