使用 XmlSerializer 将空的 xml 属性值反序列化为可为空的 int 属性 [英] Deserializing empty xml attribute value into nullable int property using XmlSerializer

查看:23
本文介绍了使用 XmlSerializer 将空的 xml 属性值反序列化为可为空的 int 属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我从第 3 方获得了一个 xml,我需要将它反序列化为 C# 对象.该 xml 可能包含值为整数类型或空值的属性:attr="11" 或 attr="".我想将此属性值反序列化为可空整数类型的属性.但是 XmlSerializer 不支持反序列化为可空类型.以下测试代码在创建 XmlSerializer 期间失败并带有 InvalidOperationException {"There was an error reflect type 'TestConsoleApplication.SerializeMe'."}.

I get an xml from the 3rd party and I need to deserialize it into C# object. This xml may contain attributes with value of integer type or empty value: attr="11" or attr="". I want to deserialize this attribute value into the property with type of nullable integer. But XmlSerializer does not support deserialization into nullable types. The following test code fails during creation of XmlSerializer with InvalidOperationException {"There was an error reflecting type '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);
    }
}

当我将值"属性的类型更改为 int 时,反序列化失败并显示 InvalidOperationException:

When I change type of 'Value' property to int, deserialization fails with InvalidOperationException:

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

There is an error in XML document (1, 16).

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

Can anybody advise how to deserialize attribute with empty value into nullable type (as a null) at the same time deserializing non-empty attribute value into the integer? Is there any trick for this so I will not have to do deserialization of each field manually (actually there are a lot of them)?

在ahsteele评论后更新:

Update after comment from ahsteele:

  1. Xsi:nil 属性

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

As far as I know, this attribute works only with XmlElementAttribute - this attribute specifies that the element has no content, whether child elements or body text. But I need to find the solution for XmlAttributeAttribute. Anyway I cannot change xml because I have no control over it.

bool *指定属性

此属性仅在属性值为非空或属性缺失时有效.当 attr 为空值 (attr='') 时,XmlSerializer 构造函数将失败(如预期).

This property works only when attribute value is non-empty or when attribute is missing. When attr has empty value (attr='') the XmlSerializer constructor fails (as expected).

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

    [XmlIgnore]
    public bool ValueSpecified;
}

  • 自定义 Nullable 类,如本博客中亚历克斯·斯科德利斯 (Alex Scordellis) 的帖子

    我尝试将这篇博文中的课程应用于我的问题:

    I tried to adopt the class from this blog post to my problem:

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

    但 XmlSerializer 构造函数因 InvalidOperationException 而失败:

    But XmlSerializer constructor fails with InvalidOperationException:

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

    Cannot serialize member 'Value' of type TestConsoleApplication.NullableInt.

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

    XmlAttribute/XmlText cannot be used to encode types implementing IXmlSerializable }

  • 丑陋的代理解决方案(我很惭愧,我在这里写了这段代码:)):

  • Ugly surrogate solution (shame on me that I wrote this code here :) ):

    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;
        }
    }
    

    但我不想提出这样的解决方案,因为它破坏了我的类为其消费者的接口.我最好手动实现 IXmlSerializable 接口.

    But I don’t want to come up with the solution like this because it breaks interface of my class for its consumers. I would better manually implement IXmlSerializable interface.

    目前看起来我必须为整个 Element 类实现 IXmlSerializable(它很大)并且没有简单的解决方法......

    Currently it looks like I have to implement IXmlSerializable for the whole Element class (it is big) and there are no simple workaround…

    推荐答案

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

    I solved this problem by implementing IXmlSerializable interface. I did not found easier way.

    这是测试代码示例:

    [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);
        }
    }
    

    这篇关于使用 XmlSerializer 将空的 xml 属性值反序列化为可为空的 int 属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆