【问题标题】:How to ignore invalid enum values during xml deserialization?如何在 xml 反序列化期间忽略无效的枚举值?
【发布时间】:2020-04-02 11:10:01
【问题描述】:

我想将一个 xml 文档反序列化为一个类,该类由相关的 xsd 文件生成。我无法控制 xml 文件的内容。

在反序列化过程中我遇到了异常,因为 xml 文档中的枚举值不符合 xsd 的要求。我希望反序列化继续进行,而不是中断,并为任何此类错误采用默认值。有什么办法可以完成这种行为吗?


编辑: 为了澄清,我想要实现的目标:我想从数字发票中读取数据。因此,xml 文件的创建是某种黑盒,即使结构符合标准,也可能包含 flase 值。但这并不意味着,每一个价值都以这种方式存在缺陷。该异常使我无法读取正确的值,因此我只想通过在发生此类错误时以某种方式插入默认值来完成反序列化。

将值标记为过时或使用 XmlIgnore 标记它们都不起作用,因为我收到的下一个 xml 可能包含正确的值。

我希望这有助于澄清问题。


现在,我使用System.Xml.Serialization dll,但我愿意实现任何可以帮助我实现所需行为的库。

我得到的异常:

"System.InvalidOperationException: 实例验证错误: 'x' is 不是 xType 的有效值.."

抛出异常的代码:

XmlSerializer serializer = new xml.XmlSerializer(typeof(MyType));
MyType invoice = serializer.Deserialize(memoryStream) as MyType;

我知道代码没有多大帮助,所以我将添加枚举,这是目前有问题的:

public enum PaymentMeansCodeContentType
    {

        [System.Xml.Serialization.XmlEnumAttribute("10")]
        Item10,

        [System.Xml.Serialization.XmlEnumAttribute("20")]
        Item20,

        [System.Xml.Serialization.XmlEnumAttribute("30")]
        Item30,

        [System.Xml.Serialization.XmlEnumAttribute("48")]
        Item48,

        [System.Xml.Serialization.XmlEnumAttribute("49")]
        Item49,

        [System.Xml.Serialization.XmlEnumAttribute("57")]
        Item57,

        [System.Xml.Serialization.XmlEnumAttribute("58")]
        Item58,

        [System.Xml.Serialization.XmlEnumAttribute("59")]
        Item59,

        ZZZ,
    }

这些是使用 xsd 命令行工具自动生成的: https://docs.microsoft.com/de-de/dotnet/standard/serialization/xml-schema-definition-tool-xsd-exe

我需要反序列化的 xml 为我提供了一个“1”,因此显然是一个无效值。我仍然需要从 xml 访问其他有效值并提供指示哪些值有缺陷的方法。

【问题讨论】:

  • 如果没有任何代码或示例数据来重现它,很难就该问题提出建议。可以修改问题以包含此内容吗?
  • @Martin 我试图更好地解释这个问题。我希望它有所帮助。我正在处理数千行的生成类,所以我认为这些示例不会有太大帮助。
  • @Florian 也许您正在寻找XmlSchemaValidator ?看看docs.microsoft.com/en-us/dotnet/standard/data/xml/…

标签: c# .net enums xml-deserialization


【解决方案1】:

我仍然无法找到我希望的简单答案,但设法找到了适合我的解决方法。我最终根据可能的值预先验证了 XML 文件中的每个枚举。如果 XML 与枚举不匹配,我将错误的值和节点保存到验证结果集中,并用枚举默认值覆盖 xml。

【讨论】:

    【解决方案2】:

    我建议你将 Enum 的值存储为字符串,然后自己解析。这个实现起来比较简单,举个例子:

    public enum MyEnum
    {
        Default, //The default value to apply in the event of an invalid Enum value
    
        [XmlEnumAttribute("10")]
        Item10,
    
        [XmlEnumAttribute("20")]
        Item20
    }
    
    public class MyClass
    {
        public string Value { get; set; }
    
        public MyEnum EnumValue => (MyEnum)(typeof(MyEnum).GetFields().FirstOrDefault(f => 
                                       f.GetCustomAttribute<XmlEnumAttribute>()?.Name == Value)?
                                           .GetValue(null) ?? MyEnum.Default);
    }
    

    或者如果你喜欢它,你也可以设置一个可为空的枚举

    public enum MyEnum
    {
        [XmlEnumAttribute("10")]
        Item10,
    
        [XmlEnumAttribute("20")]
        Item20
    }
    
    public class MyClass
    {
        public string Value { get; set; }
    
        public MyEnum? EnumValue => (MyEnum?)typeof(MyEnum).GetFields().FirstOrDefault(f =>
                                       f.GetCustomAttribute<XmlEnumAttribute>()?.Name == Value)?
                                           .GetValue(null);
    }
    

    【讨论】:

      【解决方案3】:

      您可以使用 Extended Xml Serializer 库(可通过 nuget 获得)而不是默认的 XmlSerializer,使用 custom converter 将在出现错误时设置默认值。

      【讨论】:

        【解决方案4】:

        正如 Martin 所提到的,如果没有适当的上下文或示例代码,这有点难以回答。但是,您可能希望查看模型属性上的 XmlIgnoreAttribute 装饰器。有关如何使用的更多详细信息,请参阅下面的 URL 和代码示例:

        https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmlattributes.xmlignore?view=netframework-4.8

        using System;
        using System.IO;
        using System.Xml.Serialization;
        
        // This is the class that will be serialized. 
        public class Group
        {
           // The GroupName value will be serialized--unless it's overridden.
           public string GroupName;
        
           /* This field will be ignored when serialized--
              unless it's overridden. */
           [XmlIgnoreAttribute]
           public string Comment;
        }
        

        【讨论】:

        • 它会忽略任何值,甚至是有效值
        • 就像@Innat3 提到的那样,如果其他 xml 为我提供了有效值,我需要它们。
        【解决方案5】:

        您可以将会员标记为作废

        public enum TypeEnum
        {
            Temperature,
            Pressure,
            [Obsolete]
            Humidity
        }
        

        更多信息 - docs

        【讨论】:

        • 遗憾的是这对我不起作用,因为 xml 包含的值没有出现在枚举中。
        • 这也可能破坏其他代码,如 Enum.GetValues
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-08-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-10
        • 1970-01-01
        相关资源
        最近更新 更多