Here is my class:
public class Command
{
[XmlArray(IsNullable = true)]
public List<Parameter> To { get; set; }
}
When I serialize an object of this class:
var s = new XmlSerializer(typeof(Command));
s.Serialize(Console.Out, new Command());
it prints as expected (xml header and default MS namespaces are omitted):
<Command><To xsi:nil="true" /></Command>
When I took this xml and tried to deserialize it I got stucked, because it always print "Not null":
var t = s.Deserialize(...);
if (t.To == null)
Console.WriteLine("Null");
else
Console.WriteLine("Not null");
How to force deserializer to make my list null, if it is null in xml?
If you use an array instead of a list it works as expected
public class Command
{
[XmlArray(IsNullable = true)]
public Parameter[] To { get; set; }
}
Ugh, annoying isn't it. You can see it being doing by running sgen.exe on your assembly with the /keep and /debug options so you can debug the deserialization code. It looks roughly like this:
global::Command o;
o = new global::Command();
if ((object)(o.@To) == null) o.@To = new global::System.Collections.Generic.List<global::Parameter>();
global::System.Collections.Generic.List<global::Parameter> a_0 = (global::System.Collections.Generic.List<global::Parameter>)o.@To;
// code elided
//...
while (Reader.NodeType != System.Xml.XmlNodeType.EndElement && Reader.NodeType != System.Xml.XmlNodeType.None) {
if (Reader.NodeType == System.Xml.XmlNodeType.Element) {
if (((object)Reader.LocalName == (object)id4_To && (object)Reader.NamespaceURI == (object)id2_Item)) {
if (!ReadNull()) {
if ((object)(o.@To) == null) o.@To = new global::System.Collections.Generic.List<global::Parameter>();
global::System.Collections.Generic.List<global::Parameter> a_0_0 = (global::System.Collections.Generic.List<global::Parameter>)o.@To;
// code elided
//...
}
else {
// Problem here:
if ((object)(o.@To) == null) o.@To = new global::System.Collections.Generic.List<global::Parameter>();
global::System.Collections.Generic.List<global::Parameter> a_0_0 = (global::System.Collections.Generic.List<global::Parameter>)o.@To;
}
}
}
Reader.MoveToContent();
CheckReaderCount(ref whileIterations1, ref readerCount1);
}
ReadEndElement();
return o;
No less than 3 places where it makes sure the @To property isn't null. The first one is somewhat defensible, hard to deserialize data when the structure doesn't exist. The second one does the null test again, that's the only real good one. The third one is the problem, ReadNull() returned true but it still creates a non-null property value.
If you want to differentiate between empty and null then you have no good solution but edit this code by hand. Do this only if you are really desperate and the class is 100% stable. Well, don't do it. João's solution is the only good one.
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