DataContractSerializer 未正确反序列化

问题描述 投票:0回答:1
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] in :0

我一直遇到这个问题,但我不知道为什么。我读了一遍又一遍的手册,但没有解决。我如何才能正确序列化此图书列表?

注意,我已经查看了WCF:序列化和反序列化通用集合,但建议的更改都没有解决该问题。只有一个解决方案可以解决此问题,即将 XML 中的一个元素移至下方:

<?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>

这没有任何意义,因为 Microsoft 自己的文档中没有提到排序。

它应该打印书籍列表

c# datacontractserializer
1个回答
0
投票

我用这个修复了它,例如:

public class Library {
    [DataMember(Order = 0)] public string Name { get; set; }
    [DataMember(Name = "Books", Order = 1)]
    public List<Book> books = new();
    public List<Book> Books => books;
}

现在它可以正确读取代码示例中的原始 XML 字符串。

问题是您的 XML 字符串与您的数据契约不对应。在 XML 中,

Name
Library
中的第一个元素,如果您将其移到
Books
之后,它将与您未修改的数据协定一起使用。

就是这样。您可以决定如何处理每种情况下的排序,是否避免使用

Order
参数。不管怎样,我强烈建议您至少首先使用
serializer.WriteObject
创建一个全面的 XML 数据示例。您可以将示例对象图写入文件,然后手动写入输入 XML(如果您需要的话)。

此外,我还演示了当

Books
永远不是
null
时的模式。这是正确实现对象compositionaggregation的方法。我认为您从未想象过当
Books
永远是
null
时的情况,如果它非空但为空或不为空,那就有意义。在原始代码中,您每次都需要创建一个
Book
实例,但这没有意义。

代码的另一个问题是没有为数据契约定义命名空间名称。您最好确保对同一个合约使用相同的名称,否则,XML 将在对象图的根内部获得冗余的命名空间。所以,你可以做类似的事情

static class DefinitionSet {
    internal const string dataContractNamespace = 
        "https/www.my.site.org/contracts/library";
    //...
}

[DataContract(
    Name = "Book",
    Namespace = DefinitionSet.dataContractNamespace)]
public class Book  { /* ... */ }

[DataContract(
    Name = "Book",
    Namespace = DefinitionSet.dataContractNamespace)]
public class Library { /* ... */ }

这对于合约的身份和唯一性、维护和版本控制非常重要。有一个空的命名空间很好,但不适合实际生产。

© www.soinside.com 2019 - 2024. All rights reserved.