Deserialize XML element with xsi:nil=“true” in C#

别说谁变了你拦得住时间么 提交于 2019-12-11 13:27:06

问题


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>

回答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.




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


来源:https://stackoverflow.com/questions/38018419/deserialize-xml-element-with-xsinil-true-in-c-sharp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!