Deserialize XML element with xsi:nil="true" in C#
Solution 1
If you simply want to discard the xsi:nil
attribute, set XmlElementAttribute.IsNullable = false
in the [XmlElement]
attribute on the property in the containing class that refers to location
.
E.g. if you have
[System.Xml.Serialization.XmlRootAttribute("locations", Namespace = "")]
public class LocationList
{
[XmlElement("location", IsNullable = true)]
public List<location> Locations { get; set; }
}
Manually change it to:
[System.Xml.Serialization.XmlRootAttribute("locations", Namespace = "")]
public class LocationList
{
[XmlElement("location", IsNullable = false)]
public List<location> Locations { get; set; }
}
If you want to bind to the value of xsi:nil
while simultaneously binding to the other attribute values, in addition to setting IsNullable = false
, you may add a manual property along the lines of the one shown in Can I have null attribute and other attribute at the same tag in XML created by XSD C# generated class?:
public partial class location
{
bool nil;
[XmlAttribute("nil", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public bool Nil
{
get
{
return nil;
}
set
{
nil = value;
}
}
public bool ShouldSerializeNil() { return nil == true; }
}
Sample fiddle.
You could also pre- and post-process the XML to rename the xsi:nil
attribute, as was suggested in this answer.
Solution 2
Try to use custom XmlReader
. This approach may be useful when there is no possibility to change classes used for deserialization. Also it doesn't require extra memory consumption.
public class NilIgnoreReader : XmlTextReader
{
public NilIgnoreReader(string url) : base(url) { }
bool isLocation = false;
public override bool Read()
{
bool read = base.Read();
if (NodeType == XmlNodeType.Element && LocalName == "location")
isLocation = true;
return read;
}
public override string GetAttribute(string localName, string namespaceURI)
{
if (isLocation && localName == "nil" &&
namespaceURI == "http://www.w3.org/2001/XMLSchema-instance")
{
isLocation = false;
return "false";
}
return base.GetAttribute(localName, namespaceURI);
}
}
The NilIgnoreReader
return false
for nil
attribute bound to the http://www.w3.org/2001/XMLSchema-instance
namespace from the location
elements.
Usage
var xs = new XmlSerializer(typeof(Records));
Records record;
using (var reader = new NilIgnoreReader("test.xml"))
record = (Records)xs.Deserialize(reader);
It works for XML like as
<?xml version="1.0"?>
<records xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="structure.xsd">
<locations>
<location country="PARAGUAY" city="Ciudad del Este" state="Alto Parana" xsi:nil="true"/>
<location country="BRAZIL" city="Passo Fundo" state="Rio Grande do Sul" xsi:nil="true"/>
</locations>
</records>
and classes like as
[XmlRoot("records")]
public class Records
{
[XmlArray("locations")]
[XmlArrayItem("location")]
public Location[] Locations { get; set; }
}
public class Location
{
[XmlAttribute("country")]
public string Country { get; set; }
[XmlAttribute("city")]
public string City { get; set; }
[XmlAttribute("state")]
public string State { get; set; }
}
GigiSan
Updated on June 14, 2022Comments
-
GigiSan almost 2 years
I'm trying to deserialize a XML file with
XmlSerializer
in C#.The destination class that follows was automatically generated using the xsd utility.
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)] [System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = true)] public partial class location { private string cityField; private string countryField; private string stateField; private string textField; /// <remarks/> [System.Xml.Serialization.XmlAttributeAttribute()] public string city { get { return this.cityField; } set { this.cityField = value; } } /// <remarks/> [System.Xml.Serialization.XmlAttributeAttribute()] public string country { get { return this.countryField; } set { this.countryField = value; } } /// <remarks/> [System.Xml.Serialization.XmlAttributeAttribute()] public string state { get { return this.stateField; } set { this.stateField = value; } } /// <remarks/> [System.Xml.Serialization.XmlTextAttribute()] public string Text { get { return this.textField; } set { this.textField = value; } } }
Everything works fine until I reach this portion of the file:
<locations> <location country="PARAGUAY" city="Ciudad del Este" state="Alto Parana" xsi:nil="true"/> <location country="BRAZIL" city="Passo Fundo" state="Rio Grande do Sul" xsi:nil="true"/> </locations>
As stated in the MSDN, an element with xsi:nil="true" will be deserialized as a null object, losing all attributes completely. In C# this translates in a null object.
Is there a way to change this behaviour so to have the three properties deserialized?
Thanks in advance for any advice!
EDIT 1:
This is the associated namespace:
<records xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="structure.xsd"> (location is within here somewhere) </records>