使用XmlSerializer将空的xml属性值反序列化为可为null的int属性

我从第三方得到一个XML,我需要反序列化成C#对象。 这个XML可能包含整型或空值的属性:attr =“11”或attr =“”。 我想将这个属性值反序列化到具有可为空的整数types的属性。 但是XmlSerializer不支持反序列化为可空types。 以下testing代码在创buildXmlSerializer时失败,InvalidOperationExceptionexception{“有一个错误反映types'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”属性的types更改为int时,反序列化将失败并显示InvalidOperationException:

XML文档中有错误(1,16)。

任何人都可以build议如何将空值的属性反序列化为可为空的types(作为空),同时将非空属性值反序列化为整数? 有没有这个技巧,所以我不必手动反序列化每个领域(实际上有很多)?

从ahsteele发表评论后更新:

  1. Xsi:零属性

    据我所知,这个属性只适用于XmlElementAttribute – 这个属性指定该元素没有内容,不pipe是子元素还是正文文本。 但是我需要findXmlAttributeAttribute的解决scheme。 无论如何,我不能改变XML,因为我无法控制它。

  2. 布尔*指定的属性

    此属性仅在属性值非空或属性丢失时才起作用。 当attr具有空值(attr =')XmlSerializer构造函数失败(按预期)。

     public class Element { [XmlAttribute("attr")] public int Value { get; set; } [XmlIgnore] public bool ValueSpecified; } 
  3. 自定义Nullable类,像Alex Scordellis的这篇博客文章一样

    我试图从这个博客文章采纳我的问题:

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

    但是,XmlSerializer构造函数失败,出现InvalidOperationException:

    无法序列化TestConsoleApplication.NullableInttypes的成员“值”。

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

  4. 丑陋的替代解决scheme(我在这里写这个代码:)):

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

    但我不想拿出这样的解决scheme,因为它为我们的消费者打破了我的课程界面。 我最好手动实现IXmlSerializable接口。

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

这应该工作:

 [XmlIgnore] public int? Age { get; set; } [XmlElement("Age")] public string AgeAsText { get { return (Age.HasValue) ? Age.ToString() : null; } set { Age = !string.IsNullOrEmpty(value) ? int.Parse(value) : default(int?); } } 

我通过实现IXmlSerializable接口来解决这个问题。 我没有find更简单的方法。

以下是testing代码示例:

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

我已经很久以前就搞砸了序列化,并且在处理值types的空值时发现了下面的文章和文章。

如何在C#中使用XmlSerializer创build一个值types的答案– 序列化详细描述了XmlSerializer的一个非常漂亮的技巧。 具体来说,XmlSerialier查找XXXSpecified布尔属性,以确定是否应该包括它允许您忽略空值。

亚历克斯Scordellis问一个StackOverflow问题,收到了很好的答案 。 Alex还在他的博客上写了一个关于他试图解决的问题, 用XmlSerializer反序列化成Nullable <int>

关于Xsi:nil属性绑定支持的MSDN文档也很有用。 正如IXmlSerializable接口的文档一样,尽pipe编写自己的实现应该是最后的手段。

以为我不妨把我的答案扔到帽子里:通过创build一个实现IXmlSerializable接口的自定义types来解决这个问题:

假设您有一个包含以下节点的XML对象:

 <ItemOne>10</Item2> <ItemTwo /> 

代表他们的对象:

 public class MyItems { [XmlElement("ItemOne")] public int ItemOne { get; set; } [XmlElement("ItemTwo")] public CustomNullable<int> ItemTwo { get; set; } // will throw exception if empty element and type is int } 

dynamic可空结构代表任何潜在的可空条目以及转换

 public struct CustomNullable<T> : IXmlSerializable where T: struct { private T value; private bool hasValue; public bool HasValue { get { return hasValue; } } public T Value { get { return value; } } private CustomNullable(T value) { this.hasValue = true; this.value = value; } public XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { string strValue = reader.ReadString(); if (String.IsNullOrEmpty(strValue)) { this.hasValue = false; } else { T convertedValue = strValue.To<T>(); this.value = convertedValue; this.hasValue = true; } reader.ReadEndElement(); } public void WriteXml(XmlWriter writer) { throw new NotImplementedException(); } public static implicit operator CustomNullable<T>(T value) { return new CustomNullable<T>(value); } } public static class ObjectExtensions { public static T To<T>(this object value) { Type t = typeof(T); // Get the type that was made nullable. Type valueType = Nullable.GetUnderlyingType(typeof(T)); if (valueType != null) { // Nullable type. if (value == null) { // you may want to do something different here. return default(T); } else { // Convert to the value type. object result = Convert.ChangeType(value, valueType); // Cast the value type to the nullable type. return (T)result; } } else { // Not nullable. return (T)Convert.ChangeType(value, typeof(T)); } } }