我怎样才能让 XmlSerializer Deserialize 告诉我标签名称上的拼写错误

How can I make XmlSerializer Deserialize tell me about typos on tag names

我知道大多数情况下,XmlSerializerDeserialize 方法会在出现错误(例如,打字错误)时报错。但是,我发现了一个它没有抱怨的例子,而我本以为它会抱怨;我想知道是否可以通过某种方式告知该问题。

下面的示例代码包含 3 个内容:一个按预期工作的好示例,一个会抱怨(已注释掉)的示例和一个不抱怨的示例,这是我想知道如何说明的示例有问题。

注意: 我很欣赏一种可能的途径是 XSD 验证;但这真的感觉像是一把大锤来破解看似简单的问题。例如,如果我正在编写一个反序列化器,它有不知道如何处理的意外数据,我会让我的代码抱怨它。

我使用 NUnit(NuGet 包)进行断言;但你并不真的需要它,只需注释掉 Assert 行 - 你可以看到我所期待的。

using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using NUnit.Framework;

public static class Program
{
    public static void Main()
    {
        string goodExampleXml = @"<?xml version=""1.0"" encoding=""utf-8""?><Example><Weathers><Weather>Sunny</Weather></Weathers></Example>";
        var goodExample = Load(goodExampleXml);
        Assert.That(goodExample, Is.Not.Null);
        Assert.That(goodExample.Weathers, Is.Not.Null);
        Assert.That(goodExample.Weathers, Has.Length.EqualTo(1));
        Assert.That(goodExample.Weathers.First(), Is.EqualTo(Weather.Sunny));

        string badExampleXmlWhichWillComplainXml = @"<?xml version=""1.0"" encoding=""utf-8""?><Example><Weathers><Weather>Suny</Weather></Weathers></Example>";
        // var badExampleWhichWillComplain = Load(badExampleXmlWhichWillComplainXml); // this would complain, quite rightly, so I've commented it out

        string badExampleXmlWhichWillNotComplain = @"<?xml version=""1.0"" encoding=""utf-8""?><Example><Weathers><Weathe>Sunny</Weathe></Weathers></Example>";
        var badExample = Load(badExampleXmlWhichWillNotComplain);
        Assert.That(badExample, Is.Not.Null);
        Assert.That(badExample.Weathers, Is.Not.Null);
        // clearly, the following two assertions will fail because I mis-typed the tag name; but I want to know there has been a problem before this point.
        Assert.That(badExample.Weathers, Has.Length.EqualTo(1));
        Assert.That(badExample.Weathers.First(), Is.EqualTo(Weather.Sunny));
    }

    private static Example Load(string serialized)
    {
        byte[] byteArray = Encoding.UTF8.GetBytes(serialized);
        var xmlSerializer = new XmlSerializer(typeof(Example));
        using var stream = new MemoryStream(byteArray, false);
        return (Example)xmlSerializer.Deserialize(stream);
    }
}

public enum Weather
{
    Sunny,
    Cloudy,
    Rainy,
    Windy,
    Stormy,
    Snowy,
}

public class Example
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Serialized XML")]
    [XmlArray("Weathers")]
    [XmlArrayItem("Weather")]
    public Weather[] Weathers { get; set; }
}

我测试了以下并且有效

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string xml =@"<?xml version=""1.0"" encoding=""utf-8"" ?>
                <Example>
                  <Weathers>Sunny</Weathers>
                  <Weathers>Cloudy</Weathers>
                  <Weathers>Rainy</Weathers>
                  <Weathers>Windy</Weathers>
                  <Weathers>Stormy</Weathers>
                  <Weathers>Snowy</Weathers>
                </Example>";

            StringReader sReader = new StringReader(xml);
            XmlReader reader = XmlReader.Create(sReader);

            XmlSerializer serializer = new XmlSerializer(typeof(Example));
            Example example = (Example)serializer.Deserialize(reader);
        }
    }
    public enum Weather
    {
        Sunny,
        Cloudy,
        Rainy,
        Windy,
        Stormy,
        Snowy,
    }

    public class Example
    {
        [XmlElement("Weathers")]
        public Weather[] Weathers { get; set; }
    }
}

查看了 Microsoft 为 XmlSerializer 发布的源代码后,很明显您可以订阅一些事件(这正是我所希望的);但它们并没有暴露在 XmlSerializer 本身......你必须将包含它们的结构注入到构造函数中。

所以我已经能够修改问题中的代码以拥有一个事件处理程序,该事件处理程序在遇到未知节点时被调用(这正是我所追求的)。你需要一个额外的使用,而不是问题中给出的那些...

using System.Xml;

然后这里是修改后的“加载”方法...

    private static Example Load(string serialized)
    {
        XmlDeserializationEvents events = new XmlDeserializationEvents();
        events.OnUnknownNode = (sender, e) => System.Diagnostics.Debug.WriteLine("Unknown Node: " + e.Name);

        var xmlSerializer = new XmlSerializer(typeof(Example));
        using var reader = XmlReader.Create(new StringReader(serialized));
        return (Example)xmlSerializer.Deserialize(reader, events);
    }

所以现在我只需要做一些比只向调试输出写一行更有价值的事情。

请注意,如 XmlDeserializationEvents 页面所述,还有更多活动可用,我可能会注意每一个活动。