2015-01-20 44 views
1

使用从XSD文档生成的C#类,我可以创建一个对象并成功序列化它。但是,某些属性定义了XmlDefaultValue。如果任何对象具有默认值,那么在对象序列化时不会创建这些属性。强制XmlDefaultValue值的XML序列化

这是根据documentation的预期行为。但这不是我希望它的行为。我需要在XML文档中生成所有这些属性。
我检查过任何可能会强制输出的代码属性,即使它是默认值,但我找不到这样的代码。

有什么办法可以做到这一点吗?

回答

1

您可以为特定的一组通过构建XmlAttributeOverrides指定new XmlAttributes() { XmlDefaultValue = null }每个字段或属性有DefaultValueAttribute应用序列化时类型做到这一点,然后传送到XmlSerializer(Type, XmlAttributeOverrides)构造:

var overrides = new XmlAttributeOverrides(); 
    var attrs = new XmlAttributes() { XmlDefaultValue = null }; 

    overrides.Add(typeToSerialize, propertyNameWithDefaultToIgnore, attrs); 
    var serializer = new XmlSerializer(typeToSerialize, overrides); 

注,然而,这important warning from the documentation

动态生成的程序集

为了提高性能,XML序列化基础结构动态生成程序集以序列化和反序列化指定的类型。基础设施找到并重新使用这些程序集。使用下面的构造函数时,才会出现这种情况:

XmlSerializer.XmlSerializer(类型)

XmlSerializer.XmlSerializer(类型,字符串)

如果你使用任何其他构造,同样的多个版本程序集生成并且从不卸载,这会导致内存泄漏和性能下降。最简单的解决方案是使用前面提到的两个构造函数之一。否则,您必须将程序集缓存在Hashtable中,如以下示例所示。

然而,在代码中给出的例子并没有给出任何建议如何键入哈希表。它也不是线程安全的。 (也许它来自.NET 1.0?)

以下代码为覆盖率为xml序列化器创建一个关键方案,并制造(通过反射)序列化程序,将所有属性和字段的值(如果有)重写为null,从而有效取消默认值:

public abstract class XmlSerializerKey 
{ 
    static class XmlSerializerHashTable 
    { 
     static Dictionary<object, XmlSerializer> dict; 

     static XmlSerializerHashTable() 
     { 
      dict = new Dictionary<object, XmlSerializer>(); 
     } 

     public static XmlSerializer GetSerializer(XmlSerializerKey key) 
     { 
      lock (dict) 
      { 
       XmlSerializer value; 
       if (!dict.TryGetValue(key, out value)) 
        dict[key] = value = key.CreateSerializer(); 
       return value; 
      } 
     } 
    } 

    readonly Type serializedType; 

    protected XmlSerializerKey(Type serializedType) 
    { 
     this.serializedType = serializedType; 
    } 

    public Type SerializedType { get { return serializedType; } } 

    public override bool Equals(object obj) 
    { 
     if (ReferenceEquals(this, obj)) 
      return true; 
     else if (ReferenceEquals(null, obj)) 
      return false; 
     if (GetType() != obj.GetType()) 
      return false; 
     XmlSerializerKey other = (XmlSerializerKey)obj; 
     if (other.serializedType != serializedType) 
      return false; 
     return true; 
    } 

    public override int GetHashCode() 
    { 
     int code = 0; 
     if (serializedType != null) 
      code ^= serializedType.GetHashCode(); 
     return code; 
    } 

    public override string ToString() 
    { 
     return string.Format(base.ToString() + ": for type: " + serializedType.ToString()); 
    } 

    public XmlSerializer GetSerializer() 
    { 
     return XmlSerializerHashTable.GetSerializer(this); 
    } 

    protected abstract XmlSerializer CreateSerializer(); 
} 

public abstract class XmlserializerWithExtraTypesKey : XmlSerializerKey 
{ 
    static IEqualityComparer<HashSet<Type>> comparer; 

    readonly HashSet<Type> extraTypes = new HashSet<Type>(); 

    static XmlserializerWithExtraTypesKey() 
    { 
     comparer = HashSet<Type>.CreateSetComparer(); 
    } 

    protected XmlserializerWithExtraTypesKey(Type serializedType, IEnumerable<Type> extraTypes) 
     : base(serializedType) 
    { 
     if (extraTypes != null) 
      foreach (var type in extraTypes) 
       this.extraTypes.Add(type); 
    } 

    public Type[] ExtraTypes { get { return extraTypes.ToArray(); } } 

    public override bool Equals(object obj) 
    { 
     if (!base.Equals(obj)) 
      return false; 
     XmlserializerWithExtraTypesKey other = (XmlserializerWithExtraTypesKey)obj; 
     return comparer.Equals(this.extraTypes, other.extraTypes); 
    } 

    public override int GetHashCode() 
    { 
     int code = base.GetHashCode(); 
     if (extraTypes != null) 
      code ^= comparer.GetHashCode(extraTypes); 
     return code; 
    } 
} 

public sealed class XmlSerializerIgnoringDefaultValuesKey : XmlserializerWithExtraTypesKey 
{ 
    readonly XmlAttributeOverrides overrides; 

    private XmlSerializerIgnoringDefaultValuesKey(Type serializerType, IEnumerable<Type> ignoreDefaultTypes, XmlAttributeOverrides overrides) 
     : base(serializerType, ignoreDefaultTypes) 
    { 
     this.overrides = overrides; 
    } 

    public static XmlSerializerIgnoringDefaultValuesKey Create(Type serializerType, IEnumerable<Type> ignoreDefaultTypes, bool recurse) 
    { 
     XmlAttributeOverrides overrides; 
     Type [] typesWithOverrides; 

     CreateOverrideAttributes(ignoreDefaultTypes, recurse, out overrides, out typesWithOverrides); 
     return new XmlSerializerIgnoringDefaultValuesKey(serializerType, typesWithOverrides, overrides); 
    } 

    protected override XmlSerializer CreateSerializer() 
    { 
     var types = ExtraTypes; 
     if (types == null || types.Length < 1) 
      return new XmlSerializer(SerializedType); 
     return new XmlSerializer(SerializedType, overrides); 
    } 

    static void CreateOverrideAttributes(IEnumerable<Type> types, bool recurse, out XmlAttributeOverrides overrides, out Type[] typesWithOverrides) 
    { 
     HashSet<Type> visited = new HashSet<Type>(); 
     HashSet<Type> withOverrides = new HashSet<Type>(); 
     overrides = new XmlAttributeOverrides(); 

     foreach (var type in types) 
     { 
      CreateOverrideAttributes(type, recurse, overrides, visited, withOverrides); 
     } 

     typesWithOverrides = withOverrides.ToArray(); 
    } 

    static void CreateOverrideAttributes(Type type, bool recurse, XmlAttributeOverrides overrides, HashSet<Type> visited, HashSet<Type> withOverrides) 
    { 
     if (type == null || type == typeof(object) || type.IsPrimitive || type == typeof(string) || visited.Contains(type)) 
      return; 
     var attrs = new XmlAttributes() { XmlDefaultValue = null }; 
     foreach (var property in type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public)) 
      if (overrides[type, property.Name] == null) // Check to see if overrides for this base type were already set. 
       if (property.GetCustomAttributes<DefaultValueAttribute>(true).Any()) 
       { 
        withOverrides.Add(type); 
        overrides.Add(type, property.Name, attrs); 
       } 
     foreach (var field in type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public)) 
      if (overrides[type, field.Name] == null) // Check to see if overrides for this base type were already set. 
       if (field.GetCustomAttributes<DefaultValueAttribute>(true).Any()) 
       { 
        withOverrides.Add(type); 
        overrides.Add(type, field.Name, attrs); 
       } 
     visited.Add(type); 
     if (recurse) 
     { 
      var baseType = type.BaseType; 
      if (baseType != type) 
       CreateOverrideAttributes(baseType, recurse, overrides, visited, withOverrides); 
     } 
    } 
} 

然后你会说它是这样的:

var serializer = XmlSerializerIgnoringDefaultValuesKey.Create(typeof(ClassToSerialize), new[] { typeof(ClassToSerialize), typeof(AdditionalClass1), typeof(AdditionalClass2), ... }, true).GetSerializer(); 

例如,在下面的类层次结构:

public class BaseClass 
{ 
    public BaseClass() { Index = 1; } 
    [DefaultValue(1)] 
    public int Index { get; set; } 
} 

public class MidClass : BaseClass 
{ 
    public MidClass() : base() { MidDouble = 1.0; } 
    [DefaultValue(1.0)] 
    public double MidDouble { get; set; } 
} 

public class DerivedClass : MidClass 
{ 
    public DerivedClass() : base() { DerivedString = string.Empty; } 
    [DefaultValue("")] 
    public string DerivedString { get; set; } 
} 

public class VeryDerivedClass : DerivedClass 
{ 
    public VeryDerivedClass() : base() { this.VeryDerivedIndex = -1; } 
    [DefaultValue(-1)] 
    public int VeryDerivedIndex { get; set; } 
} 

默认XmlSerializer生产:

<VeryDerivedClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" /> 

但自定义序列产生

<?xml version="1.0" encoding="utf-16"?> 
<VeryDerivedClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <Index>1</Index> 
    <MidDouble>1</MidDouble> 
    <DerivedString /> 
    <VeryDerivedIndex>-1</VeryDerivedIndex> 
</VeryDerivedClass> 

最后,要注意由[XmlElement(IsNullable = true)]所以写零点控制空值的,写作是不受此串行器。

0

关于DataContract最后的答案是没有答案的。 XSD是自动生成的,消费类的人不能控制原作者使用的属性。问题是关于基于XSD的自动生成的类。

其他答案也有问题,因为定义了默认值的属性也可能不允许空值(这经常发生)。唯一真正的解决方案是有一个序列化器,你可以告诉它序列化时要忽略哪些属性。对于当前的XML序列化器来说,这一直是一个严重的问题,而这些问题根本无法让人们传递要强制序列化的属性。

实际场景:

REST服务在主体中接受XML以更新对象。 XML具有由其余服务的作者定义的XSD。剩余服务存储的当前对象具有非默认值集。用户修改XML以将其更改回默认值......但放入REST帖子正文中的序列化版本跳过该值并且不包含该值,因为它将其设置为默认值。

什么是泥潭......无法更新值,因为不导出默认值的逻辑完全忽略了XML可用于更新对象的想法,而不仅仅是基于XML创建新的对象。我不敢相信这么多年,没有人修改XML序列化器来轻松处理这个基本的场景。

+0

不,我回答了我自己的问题。 DataContract属性正是我最初寻找的正确答案。 DataContract是从XSD生成的,但它不是一个连续的构建,只是一次生成,所以在第一次构建它之后,自定义修改该类没有问题。 – Wedge 2017-05-22 18:16:09