You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 15:48:10