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?); }
}
I solved this problem by implementing IXmlSerializable interface. I did not found easier way.
Here is the test code sample:
[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);
}
}
I've been messing around with serialization a lot myself of late and have found the following articles and posts helpful when dealing with null data for value types.
The answer to How to make a value type nullable with XmlSerializer in C# - serialization details a pretty nifty trick of the XmlSerializer. Specifically, the XmlSerialier looks for a XXXSpecified boolean property to determine if it should be included which allows you to ignore nulls.
Alex Scordellis asked a StackOverflow question which received a good answer. Alex also did a good writeup on his blog about the problem he was trying to solve Using XmlSerializer to deserialize into a Nullable<int>.
The MSDN documentation on Xsi:nil Attribute Binding Support is also useful. As is the documentation on IXmlSerializable Interface, though writing your own implementation should be your last resort.
Thought I might as well throw my answer into the hat: Solved this issue by creating a custom type that implements the IXmlSerializable interface:
Say you have a an XML object with the following nodes:
<ItemOne>10</Item2>
<ItemTwo />
The object to represent them:
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 nullable struct to represent any potential nullable entries along with conversion
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));
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With