Deserialize an empty xml attribute value into a nullable int property using XmlSerializer

I get xml from a third party and I need to deserialize it to a C # object. This xml can contain attributes with an integer value or an empty value: attr = "11" or attr = ". I want to deserialize this attribute value in a property with an integer integer value type. But XmlSerializer does not support deserialization into types with a zero value. The following test the code crashes while creating an XmlSerializer with InvalidOperationException {"An error occurred reflecting the 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); } } 

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

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

Can someone advise how to deserialize an attribute with an empty value into a nullable type (like null) while deserializing a non-empty attribute value into an integer? Is there any trick for this, so I don’t have to do deserialization of each field manually (there are actually a lot of them)?

Update after comment from ahsteele:

  • Xsi: nil attribute

    As far as I know, this attribute only works with XmlElementAttribute - this attribute indicates that the element has no content, whether it be child elements or body text. But I need to find a solution for XmlAttributeAttribute. In any case, I can not change the xml, because I can not control it.

  • bool * Set property

    This property only works when the attribute value is not empty or when the attribute is absent. When attr is empty (attr = ``), the XmlSerializer constructor fails (as expected).

     public class Element { [XmlAttribute("attr")] public int Value { get; set; } [XmlIgnore] public bool ValueSpecified; } 
  • Custom Nullable Class, as in this blog by Alex Scordellis

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

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

    But the XmlSerializer constructor does not work with InvalidOperationException:

    Cannot serialize the value element of type TestConsoleApplication.NullableInt.

    XmlAttribute / XmlText cannot be used to encode types that implement IXmlSerializable}

  • An ugly surrogate solution (I am ashamed for 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; } } 

    But I do not want to come up with a solution like this because it violates the interface of my class for its consumers. I'd rather manually implement the IXmlSerializable interface.

Currently, it looks like I should implement IXmlSerializable for the whole Element class (it's big), and there is no simple workaround ...

+57
xml serialization nullable
Aug 18 '09 at 18:35
source share
4 answers

I solved this problem by implementing the IXmlSerializable interface. I did not find an easier way.

Here is a sample test code:

 [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); } } 
+17
Aug 20 '09 at 9:18
source share

This should work:

 [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?); } } 
+42
Sep 25 '09 at 19:36
source share

Recently, I have very often bothered with serialization and found the following articles and posts useful when working with null data for value types.

The answer to How to make a value type nullable with an XmlSerializer in C # serialization is a pretty neat XmlSerializer trick. In particular, XmlSerialier looks for the XXXSpecified boolean property to determine if it should be included, which allows zeros to be ignored.

Alex Skordellis asked a question from StackOverflow, which received a good answer . Alex also made a good blog entry about the problem he was trying to solve Using XmlSerializer for deserialization in a Nullable <int> .

Xsi: nil MSDN documentation Attribute binding support is also helpful. Like the IXmlSerializable Interface documentation, although your own implementation should be your last resort.

+9
Aug 18 '09 at 18:57
source share

Thought that I could also throw my answer in the hat: This problem was solved by creating a custom type that implements the IXmlSerializable interface:

Say you have an XML object with the following nodes:

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

Object for their presentation:

 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 null structure to represent any potential null entries along with the transformation

 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)); } } } 
+2
Jan 16 '17 at 23:12
source share



All Articles