使用DataContractSerializer反序列化XML时出错

时间:2017-07-10 09:30:31

标签: c# xml serialization 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,从那时起我在序列化/反序列化的类中添加/删除了一些属性。

我有以下例外:

  

反序列化的对象,引用ID为' i53'在溪流中找不到。

是否可以自定义DataContractSerializer,只是跳过属性,如果它不在模型中?请注意,deleted属性是对另一个复杂对象的引用,而不是对简单类型的引用。 XML文件包含了我的课程,不再是我的课程了。

1 个答案:

答案 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",序列化后将不再显示已废弃的元素。