【问题标题】:Deserialize XML element with xsi:nil="true" in C#在 C# 中使用 xsi:nil="true" 反序列化 XML 元素
【发布时间】:2016-06-24 16:47:25
【问题描述】:

我正在尝试在 C# 中使用 XmlSerializer 反序列化 XML 文件。

随后的目标类是使用 xsd 实用程序自动生成的。

    [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;
            }
        }
    }

在我到达文件的这一部分之前一切正常:

<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>

作为stated in the MSDN,具有 xsi:nil="true" 的元素将被反序列化为空对象,完全丢失所有属性。在 C# 中,这转换为一个空对象。

有没有办法改变这种行为以反序列化三个属性?

提前感谢您的任何建议!

编辑 1:

这是关联的命名空间:

<records xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="structure.xsd">
    (location is within here somewhere)
</records>

【问题讨论】:

  • xml 格式不正确。前缀xsi 未绑定到任何命名空间。
  • 它有一个规范,我只是为了商业隐私而省略了它。我添加了一个修改版本。
  • 并非如此。我正在尝试反序列化,而他正在尝试序列化。但是感谢您指出这一点。
  • @dbc 你是provvidential。我从那个答案中得到了建议,想出了一个解决方案。我一爬出这个泥池就会发布它。 >_

标签: c# .net xml deserialization xmlserializer


【解决方案1】:

如果您只想丢弃xsi:nil 属性,请在引用location 的包含类中的属性的[XmlElement] 属性中设置XmlElementAttribute.IsNullable = false。 例如。如果你有

[System.Xml.Serialization.XmlRootAttribute("locations", Namespace = "")]
public class LocationList
{
    [XmlElement("location", IsNullable = true)]
    public List<location> Locations { get; set; }
}

手动改为:

[System.Xml.Serialization.XmlRootAttribute("locations", Namespace = "")]
public class LocationList
{
    [XmlElement("location", IsNullable = false)]
    public List<location> Locations { get; set; }
}

如果你想绑定xsi:nil的值同时绑定到其他属性值,除了设置IsNullable = false,你可以添加一个手动属性一个显示在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; }
}

示例fiddle

您还可以按照 this answer 中的建议对 XML 进行预处理和后处理以重命名 xsi:nil 属性。

【讨论】:

  • 这正是我的想法和我在 cmets 中提到的想法,但是当它遇到没有 xsi:nil 的 &lt;location&gt; 时似乎会中断,为所有后续 &lt;location&gt;s 设置 null .
  • @GigiSan - 您可以编辑您的问题以包含minimal reproducible example 来解决您现在看到的问题吗?我更新了我的fiddle,但我没有看到它。
  • 抱歉让您久等了。我检查了我在代码中所做的更改,发现我将IsNullable = false 放在Location 类而不是Locations 列表中(实际上对我来说似乎更合乎逻辑)。我还加了ShouldSerializeNil()方法但是没看清楚,是不是标准的序列化方法?无论如何,感谢您的帮助。由于这与我所做的相似并且已经知道它可以工作,因此我将其标记为答案。
  • @GigiSan - ShouldSerializeNil() 防止xsi:nil 属性被序列化,除非值为true。发出错误值是多余的,通常不会这样做。至于它为什么起作用,请参阅here
【解决方案2】:

尝试使用自定义XmlReader。当无法更改用于反序列化的类时,此方法可能很有用。而且它不需要额外的内存消耗。

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);
    }
}

NilIgnoreReader 返回 false 用于从 location 元素绑定到 http://www.w3.org/2001/XMLSchema-instance 命名空间的 nil 属性。

用法

var xs = new XmlSerializer(typeof(Records));
Records record;

using (var reader = new NilIgnoreReader("test.xml"))
    record = (Records)xs.Deserialize(reader);

它适用于像as这样的XML

<?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>

以及像 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 - 是的,当然,彻底测试它。我不确定这种方法是否完全正确。
  • 我选择使用 dbc 的答案,因为它与我的想法很接近,并且几乎可以肯定结果。鉴于我的时间很短,我无法在所有情况下快速测试您的代码。但无论如何,它在我尝试的第一个案例中确实有效,我认为这两种解决方案同样有效。所以非常感谢你的努力。 :)
猜你喜欢
  • 2019-02-18
  • 2015-03-10
  • 2011-02-27
  • 2021-02-07
  • 2021-09-06
  • 2011-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多