DataContractSerializer反序列化列表时出现空引用,如何解决?
以下是我的C#代码,尝试用DataContractSerializer反序列化XML为Library对象:
using System; using System.Runtime.Serialization; using System.Xml; using System.IO; using System.Collections.Generic; [DataContract(Name = "Book", Namespace = "")] public class Book { [DataMember] public string Title { get; set; } [DataMember] public string Author { get; set; } [DataMember] public int Year { get; set; } } [DataContract(Name = "Library", Namespace = "")] public class Library { [DataMember] public string Name { get; set; } [DataMember] public List<Book> Books { get; set; } } public class Program { static string xml = @"<?xml version=""1.0"" encoding=""utf-8""?> <Library xmlns:i=""http://www.w3.org/2001/XMLSchema-instance""> <Name>Central Library</Name> <Books> <Book> <Title>The Great Gatsby</Title> <Author>F. Scott Fitzgerald</Author> <Year>1925</Year> </Book> <Book> <Title>To Kill a Mockingbird</Title> <Author>Harper Lee</Author> <Year>1960</Year> </Book> </Books> </Library>"; static void Main(string[] args) { try { Library library = DeserializeLibrary(xml); Console.WriteLine($"Deserialized Library: {library.Name}"); foreach (var book in library.Books) { Console.WriteLine($"Book: {book.Title} by {book.Author} ({book.Year})"); } } catch (Exception e) { Console.WriteLine($"Deserialization failed: {e.Message}"); Console.WriteLine($"Stack trace: {e.StackTrace}"); } Console.WriteLine("Press any key to exit..."); Console.ReadKey(); } /// <summary> /// Deserializes the XML string into a Library object using DataContractSerializer /// </summary> static Library DeserializeLibrary(string xmlString) { DataContractSerializer serializer = new DataContractSerializer(typeof(Library)); using (StringReader stringReader = new StringReader(xmlString)) using (XmlReader xmlReader = XmlReader.Create(stringReader)) { return (Library)serializer.ReadObject(xmlReader); } } }
运行后出现错误:
反序列化的库:中央图书馆 反序列化失败:未将对象引用设置到对象的实例 堆栈跟踪: 在 Program.Main (System.String[] args) [0x0002a] 中 :0
我发现只有把XML元素顺序调整为<Books>在前、<Name>在后时才能正常反序列化:
<?xml version="1.0" encoding="utf-8"?> <Library xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <Books> <Book> <Title>The Great Gatsby</Title> <Author>F. Scott Fitzgerald</Author> <Year>1925</Year> </Book> <Book> <Title>To Kill a Mockingbird</Title> <Author>Harper Lee</Author> <Year>1960</Year> </Book> </Books> <Name>Central Library</Name> </Library>
但微软官方文档没提元素顺序要求,我想知道怎么解决这个问题,让程序能正常处理原XML的顺序。
这是因为**DataContractSerializer默认对元素顺序有严格要求**:当你没有给[DataMember]指定Order属性时,序列化器会按照成员名称的字母顺序来处理元素。你的Library类里,Books(B开头)的字母顺序在Name(N开头)前面,所以序列化器反序列化时会期望XML里<Books>元素出现在<Name>之前。如果XML顺序反过来,序列化器会忽略<Books>元素,导致library.Books为null,遍历的时候就触发空引用异常。
有三种可行的方案:
1. 给[DataMember]指定Order属性,匹配XML元素顺序
修改Library类的成员声明,给每个[DataMember]加上Order参数,让顺序和XML里的<Name>在前、<Books>在后一致:
[DataContract(Name = "Library", Namespace = "")] public class Library { [DataMember(Order = 1)] public string Name { get; set; } [DataMember(Order = 2)] public List<Book> Books { get; set; } }
这样序列化器就会按照你指定的顺序来处理XML元素,不受字母顺序影响。
2. 改用XmlSerializer(不要求元素顺序)
如果你不想纠结顺序问题,可以换成XmlSerializer,它不强制要求XML元素和类成员的顺序一致。修改DeserializeLibrary方法:
static Library DeserializeLibrary(string xmlString) { XmlSerializer serializer = new XmlSerializer(typeof(Library)); using (StringReader stringReader = new StringReader(xmlString)) { return (Library)serializer.Deserialize(stringReader); } }
注意:使用XmlSerializer时,类不需要[DataContract]和[DataMember]特性(保留也不影响,但推荐去掉或换成[Serializable])。
3. 调整XML元素顺序(已验证可行的方法)
保持代码不变,把XML里的<Books>移到<Name>前面,和序列化器默认的字母顺序匹配。
内容的提问来源于stack exchange,提问作者David Adams

