我从第三方获得一个xml,我需要将其反序列化为C#object.此xml可能包含值为整型或空值的属性:attr="11"或attr="".我想将这个属性值反序列化为类型为nullable integer的属性.但XmlSerializer不支持反序列化为可为空的类型.以下测试代码在创建带有InvalidOperationException的XmlSerializer期间失败{"出现了一个反映类型"TestConsoleApplication.SerializeMe."的错误.".

[XmlRoot("root")]
public class SerializeMe
{
    [XmlElement("element")]
    public Element Element { get; set; }
}

public class Element
{
    [XmlAttribute("attr")]
    public int? Value { get; set; }
}

class Program {
    static void Main(string[] args) {
        string xml = "<root><element attr=''>valE</element></root>";
        var deserializer = new XmlSerializer(typeof(SerializeMe));
        Stream xmlStream = new MemoryStream(Encoding.ASCII.GetBytes(xml));
        var result = (SerializeMe)deserializer.Deserialize(xmlStream);
    }
}

当我将"Value"属性的类型更改为int时,反序列化失败,出现InvalidOperationException:

XML文档(%1,%16)中存在错误.

有人能建议如何在将非空属性值反序列化为整数的同时,将具有空值的属性反序列化为可空类型(作为null)?有什么诀窍可以让我不必手动反序列化每个字段(实际上有很多字段)?

ahsteele发表 comments 后更新:

  1. Xsi:nil attribute

    据我所知,该属性仅适用于XmlElementAttribute——该属性指定元素没有内容,无论是子元素还是正文.但我需要找到XmlAttributeAttribute的解决方案.无论如何,我无法更改xml,因为我无法控制它.

  2. bool *Specified property

    仅当属性值非空或缺少属性时,此属性才起作用.当attr具有空值(attr=‘’)时,XmlSerializer构造函数失败(正如预期的那样).

    public class Element
    {
        [XmlAttribute("attr")]
        public int Value { get; set; }
    
        [XmlIgnore]
        public bool ValueSpecified;
    }
    
  3. Custom Nullable class like in this blog post by Alex Scordellis

    我试图采用这篇博客文章中的课程来解决我的问题:

    [XmlAttribute("attr")]
    public NullableInt Value { get; set; } 
    

    但XmlSerializer构造函数失败,并引发InvalidOperationException:

    无法序列化TestConsoleApplication类型的成员"Value".NullableInt.

    XmlAttribute/XmlText不能用于编码实现IXmlSerializable}的类型

  4. 难看的代理解决方案(我在这里编写了这段代码,这是我的耻辱:):

    public class Element
    {
        [XmlAttribute("attr")]
        public string SetValue { get; set; }
    
        public int? GetValue()
        {
            if ( string.IsNullOrEmpty(SetValue) || SetValue.Trim().Length <= 0 )
                return null;
    
            int result;
            if (int.TryParse(SetValue, out result))
                return result;
    
            return null;
        }
    }
    

    但我不想提出这样的解决方案,因为它 destruct 了我的类对其用户的接口.我最好手动实现IXmlSerializable接口.

目前看来,我必须为整个元素类实现IXmlSerializable(它很大),而且没有简单的解决方法…

推荐答案

我通过实现IXmlSerializable接口解决了这个问题.我没有找到更简单的方法.

以下是测试代码示例:

[XmlRoot("root")]
public class DeserializeMe {
    [XmlArray("elements"), XmlArrayItem("element")]
    public List<Element> Element { get; set; }
}

public class Element : IXmlSerializable {
    public int? Value1 { get; private set; }
    public float? Value2 { get; private set; }

    public void ReadXml(XmlReader reader) {
        string attr1 = reader.GetAttribute("attr");
        string attr2 = reader.GetAttribute("attr2");
        reader.Read();

        Value1 = ConvertToNullable<int>(attr1);
        Value2 = ConvertToNullable<float>(attr2);
    }

    private static T? ConvertToNullable<T>(string inputValue) where T : struct {
        if ( string.IsNullOrEmpty(inputValue) || inputValue.Trim().Length == 0 ) {
            return null;
        }

        try {
            TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));
            return (T)conv.ConvertFrom(inputValue);
        }
        catch ( NotSupportedException ) {
            // The conversion cannot be performed
            return null;
        }
    }

    public XmlSchema GetSchema() { return null; }
    public void WriteXml(XmlWriter writer) { throw new NotImplementedException(); }
}

class TestProgram {
    public static void Main(string[] args) {
        string xml = @"<root><elements><element attr='11' attr2='11.3'/><element attr='' attr2=''/></elements></root>";
        XmlSerializer deserializer = new XmlSerializer(typeof(DeserializeMe));
        Stream xmlStream = new MemoryStream(Encoding.ASCII.GetBytes(xml));
        var result = (DeserializeMe)deserializer.Deserialize(xmlStream);
    }
}

.net相关问答推荐

.NET最小API BadRequest响应不返回正文

EFCore.DbSet.Update 方法添加新行而不是更新它

如何在 C# 中将 HTTP 发布请求发送到另一个 API?

dotnet ef dbcontext scaffold command --data-annotations 或 -d 命令行参数似乎不起作用

.NET 中的线程安全集合

C# 中的 myCustomer.GetType() 和 typeof(Customer) 有什么区别?

为什么 LINQ .Where(predicate).First() 比 .First(predicate) 快?

InternalsVisibleTo 属性不起作用

在 .NET (C#) 中本地存储数据的最佳方式

检索字典值最佳实践

System.Speech.Recognition 和 Microsoft.Speech.Recognition 有什么区别?

在同一解决方案中引用 2 个不同版本的 log4net

等效于 C# 在 powershell 中的使用关键字?

String.Join 与 StringBuilder:哪个更快?

如何从文件中删除单个属性(例如只读)?

如果锁定的对象内部发生异常,它会保持锁定状态吗?

如何从 HashSet 中检索实际项目?

从 bcp 客户端接收到 colid 6 的无效列长度

判断数据表中是否包含空值的最佳方法

什么时候使用 Tuple 和 KeyValuePair 比较好?