如何将没有值的复杂XML元素反序列化为null?

时间:2016-05-22 02:18:49

标签: c# xml serialization

<Customer>
<FirstName>SomeValue</FirstName>
<LastName>Somevalue</LastName>
<Address/>
</Customer>

我有上面的xml,Address元素是一个复杂的对象。当我尝试反序列化为C#对象时,地址对象中的所有属性都设置为null,但我的要求是将其自身的地址对象设置为null。有没有办法实现这个目标?

2 个答案:

答案 0 :(得分:2)

要确认您的问题,您要问:在反序列化XML时,我是否可以将空元素映射到null属性值,但是将非空元素映射到非{{1}属性值?

假设您使用的是XmlSerializer,则不会开箱即用。当属性{http://www.w3.org/2001/XMLSchema}nil(通常缩写为null)具有值XmlSerializer时,null只会将元素映射到xsi:nil值。来自Xsi:nil Attribute Binding Support

  

XmlSerializer类将 nil 属性的真值等同于空引用,执行以下运行时转换:

     
      
  • 将XML文档反序列化为对象时:如果XmlSerializer类遇到指定xsi:nil =&#34; true&#34;的XML元素,则会将null引用(如果适用)分配给相应的对象。
  •   
  • 将对象序列化为XML文档时:如果XmlSerializer类遇到对应于XML元素的对象的空引用,则它会生成一个指定xsi的元素:nil =&#34; true&#34;或者完全离开元素,取决于nillable =&#34; true&#34;设置适用。
  •   

由于您的空"true"元素中不存在xsi:nil,因此它不会自动映射到<Address>

作为一种解决方法,您可以引入额外的&#34;代理&#34;将null转换为Address的属性,在反序列化期间,检查所设置的Customer是否具有默认值,如果是,则将基础地址值设置为Address。例如,如果您的类看起来像:

null

你可以通过以下几种方式做到这一点:

  1. 反思。介绍以下扩展方法:

    public class Address
    {
        public string Street1 { get; set; }
        public string Street2 { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string Zip { get; set; }
        public string Country { get; set; }
    }
    
    public class Customer
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public Address Address { get; set; }
    }
    

    然后按如下方式修改public static class ObjectExtensions { public static IEnumerable<object> Members(this object obj) { if (obj == null) return new object[0]; var type = obj.GetType(); return type.GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => p.GetIndexParameters().Length == 0 && p.GetGetMethod(true) != null && p.CanRead) .Select(p => p.GetValue(obj, new object[0])) .Concat(type.GetFields().Select(f => f.GetValue(obj))); } public static bool IsDefault(this object obj) { if (obj == null) return true; var type = obj.GetType(); if (type.IsValueType) { return obj.Equals(Activator.CreateInstance(type, true)); } return false; } public static bool MembersAreDefault(this object obj) { return obj.Members().All(v => v.IsDefault()); } }

    Customer

    属性public class Customer { public string FirstName { get; set; } public string LastName { get; set; } [XmlIgnore] public Address Address { get; set; } [XmlElement("Address")] [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)] public Address SerializableAddress { get { return Address; } set { if (value != null && value.MembersAreDefault()) value = null; Address = value; } } } 使用反射检查传入的SerializableAddress是否具有任何非默认值。如果不是,则将基础属性设置为null。

    此解决方案功能强大且可重复使用,但由于它使用反射,性能可能不会很好。

  2. 使用标准接口检查是否已在给定类型中设置了任何属性。首先,介绍以下内容:

    Address

    接下来,为public interface IHasDefaultMemberValues { bool MembersAreDefault(); } 实现该接口:

    Address

    最后,将public class Address : IHasDefaultMemberValues { // Properties as above. #region IHasDefaultMemberValues Members public virtual bool MembersAreDefault() { return Street1 == null && Street2 == null && City == null && State == null && Zip == null && Country == null; } #endregion } 修改为与解决方案#1完全相同。

    此解决方案具有高性能,但在将新属性添加到Customer时依赖于MembersAreDefault()正确维护。

答案 1 :(得分:-1)

您可以使用 XmlIgnore 属性排除对象的属性

[XmlIgnore] 
public Address {get; set;}