如何使用 XmlSerializer 使用 DefaultValueAttribute 序列化属性?
How to serialize properties with DefaultValueAttribute using XmlSerializer?
我正在使用 XmlSerializer
将 C# 对象序列化为 XML。我在 类 的某些属性上有 DefaultValueAttribute
我正在尝试序列化,当我尝试序列化它们时,似乎 XmlSerializer
不包含 xml 中的值,如果它等于默认值。
看这个例子:
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace Test
{
public class Person
{
[System.Xml.Serialization.XmlAttribute()]
[System.ComponentModel.DefaultValue("John")]
public string Name { get; set; }
}
public static class Test
{
public static void Main()
{
var serializer = new XmlSerializer(typeof(Person));
var person = new Person { Name = "John" };
using (var sw = new StringWriter())
{
using (var writer = XmlWriter.Create(sw))
{
serializer.Serialize(writer, person);
var xml = sw.ToString();
}
}
}
}
}
它将产生以下内容xml(注意名称属性不可用):
<?xml version="1.0" encoding="utf-16"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
我无法修改 类 的源代码,所以我无法删除 DefaultValueAttribute
。有没有办法在不更改源代码的情况下使 XmlSerializer
序列化此属性?
您可以在创建时将 XmlAttributeOverrides
实例传递给 XmlSerializer
来实现,如下所示。您可以使用此方法将默认值更改为其他值,或将其设置为 null 以有效删除它。
XmlAttributeOverrides attributeOverrides = new XmlAttributeOverrides();
var attributes = new XmlAttributes()
{
XmlDefaultValue = null,
XmlAttribute = new XmlAttributeAttribute()
};
attributeOverrides.Add(typeof(Person), "Name", attributes);
var serializer = new XmlSerializer(typeof(Person), attributeOverrides);
var person = new Person { Name = "John" };
using (var sw = new StringWriter())
{
using (var writer = XmlWriter.Create(sw))
{
serializer.Serialize(writer, person);
var xml = sw.ToString();
}
}
更新:以上意味着您必须在每个要更改的 属性 上再次提供其他不相关的属性。如果您有很多属性并且只想删除所有属性的默认值,这会有点麻烦。下面的 class 可用于保留其他自定义属性,同时仅删除一种类型的属性。如果需要只对某些属性等进行扩展,它可以进一步扩展。
public class XmlAttributeOverrideGenerator<T>
{
private static XmlAttributeOverrides _overrides;
private static Type[] _ignoreAttributes = new Type[] { typeof(DefaultValueAttribute) };
static XmlAttributeOverrideGenerator()
{
_overrides = Generate();
}
public static XmlAttributeOverrides Get()
{
return _overrides;
}
private static XmlAttributeOverrides Generate()
{
var xmlAttributeOverrides = new XmlAttributeOverrides();
Type targetType = typeof(T);
foreach (var property in targetType.GetProperties())
{
XmlAttributes propertyAttributes = new XmlAttributes(new CustomAttribProvider(property, _ignoreAttributes));
xmlAttributeOverrides.Add(targetType, property.Name, propertyAttributes);
}
return xmlAttributeOverrides;
}
public class CustomAttribProvider : ICustomAttributeProvider
{
private PropertyInfo _prop = null;
private Type[] _ignoreTypes = null;
public CustomAttribProvider(PropertyInfo property, params Type[] ignoreTypes)
{
_ignoreTypes = ignoreTypes;
_prop = property;
}
public object[] GetCustomAttributes(bool inherit)
{
var attribs = _prop.GetCustomAttributes(inherit);
if (_ignoreTypes == null) return attribs;
return attribs.Where(attrib => IsAllowedType(attrib)).ToArray();
}
private bool IsAllowedType(object attribute)
{
if (_ignoreTypes == null) return true;
foreach (Type type in _ignoreTypes)
if (attribute.GetType() == type)
return false;
return true;
}
public object[] GetCustomAttributes(Type attributeType, bool inherit)
{
throw new NotImplementedException();
}
public bool IsDefined(Type attributeType, bool inherit)
{
throw new NotImplementedException();
}
}
}
用法:
XmlAttributeOverrides attributeOverrides = XmlAttributeOverrideGenerator<Person>.Get();
var serializer = new XmlSerializer(typeof(Person), attributeOverrides);
我没有足够的声誉来评论 steve16351 的回答。但我觉得我对他的代码做了一点改进。
我的用例涉及通过 Microsoft 提供的 XSD.exe 生成的 XML 架构文件,然后从架构文件生成 class。所以 class 上有架构中的所有 DefaultValue 标签。还创建了许多嵌套类型。因此,为了使我的代码简洁,我修改了 'Generate' 方法以获取 top-level class 及其下方的所有方法的覆盖。然后所有内容都在单个 XmlAttributesOverrides 对象中返回。
static XmlAttributeOverrideGenerator()
{
_overrides = Generate(typeof(T), new XmlAttributeOverrides());
}
private static XmlAttributeOverrides Generate(Type targetType, XmlAttributeOverrides Overrides)
{
foreach (var property in targetType.GetProperties())
{
if (property.CustomAttributes.Any( CA => CA.AttributeType == typeof(DefaultValueAttribute)))
{
XmlAttributes propertyAttributes = new XmlAttributes(new CustomAttribProvider(property, _ignoreAttributes));
Overrides.Add(targetType, property.Name, propertyAttributes);
}
else
{
Type propType = property.PropertyType;
if (propType.IsClass && !propType.IsValueType && !propType.IsEnum && !propType.IsPrimitive && !propType.IsSealed) // Check if this is a custom class
{
//If this is a nested class or other class type, generate its overrides as well
Generate(propType, Overrides);
}
}
}
return Overrides;
}
我正在使用 XmlSerializer
将 C# 对象序列化为 XML。我在 类 的某些属性上有 DefaultValueAttribute
我正在尝试序列化,当我尝试序列化它们时,似乎 XmlSerializer
不包含 xml 中的值,如果它等于默认值。
看这个例子:
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace Test
{
public class Person
{
[System.Xml.Serialization.XmlAttribute()]
[System.ComponentModel.DefaultValue("John")]
public string Name { get; set; }
}
public static class Test
{
public static void Main()
{
var serializer = new XmlSerializer(typeof(Person));
var person = new Person { Name = "John" };
using (var sw = new StringWriter())
{
using (var writer = XmlWriter.Create(sw))
{
serializer.Serialize(writer, person);
var xml = sw.ToString();
}
}
}
}
}
它将产生以下内容xml(注意名称属性不可用):
<?xml version="1.0" encoding="utf-16"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
我无法修改 类 的源代码,所以我无法删除 DefaultValueAttribute
。有没有办法在不更改源代码的情况下使 XmlSerializer
序列化此属性?
您可以在创建时将 XmlAttributeOverrides
实例传递给 XmlSerializer
来实现,如下所示。您可以使用此方法将默认值更改为其他值,或将其设置为 null 以有效删除它。
XmlAttributeOverrides attributeOverrides = new XmlAttributeOverrides();
var attributes = new XmlAttributes()
{
XmlDefaultValue = null,
XmlAttribute = new XmlAttributeAttribute()
};
attributeOverrides.Add(typeof(Person), "Name", attributes);
var serializer = new XmlSerializer(typeof(Person), attributeOverrides);
var person = new Person { Name = "John" };
using (var sw = new StringWriter())
{
using (var writer = XmlWriter.Create(sw))
{
serializer.Serialize(writer, person);
var xml = sw.ToString();
}
}
更新:以上意味着您必须在每个要更改的 属性 上再次提供其他不相关的属性。如果您有很多属性并且只想删除所有属性的默认值,这会有点麻烦。下面的 class 可用于保留其他自定义属性,同时仅删除一种类型的属性。如果需要只对某些属性等进行扩展,它可以进一步扩展。
public class XmlAttributeOverrideGenerator<T>
{
private static XmlAttributeOverrides _overrides;
private static Type[] _ignoreAttributes = new Type[] { typeof(DefaultValueAttribute) };
static XmlAttributeOverrideGenerator()
{
_overrides = Generate();
}
public static XmlAttributeOverrides Get()
{
return _overrides;
}
private static XmlAttributeOverrides Generate()
{
var xmlAttributeOverrides = new XmlAttributeOverrides();
Type targetType = typeof(T);
foreach (var property in targetType.GetProperties())
{
XmlAttributes propertyAttributes = new XmlAttributes(new CustomAttribProvider(property, _ignoreAttributes));
xmlAttributeOverrides.Add(targetType, property.Name, propertyAttributes);
}
return xmlAttributeOverrides;
}
public class CustomAttribProvider : ICustomAttributeProvider
{
private PropertyInfo _prop = null;
private Type[] _ignoreTypes = null;
public CustomAttribProvider(PropertyInfo property, params Type[] ignoreTypes)
{
_ignoreTypes = ignoreTypes;
_prop = property;
}
public object[] GetCustomAttributes(bool inherit)
{
var attribs = _prop.GetCustomAttributes(inherit);
if (_ignoreTypes == null) return attribs;
return attribs.Where(attrib => IsAllowedType(attrib)).ToArray();
}
private bool IsAllowedType(object attribute)
{
if (_ignoreTypes == null) return true;
foreach (Type type in _ignoreTypes)
if (attribute.GetType() == type)
return false;
return true;
}
public object[] GetCustomAttributes(Type attributeType, bool inherit)
{
throw new NotImplementedException();
}
public bool IsDefined(Type attributeType, bool inherit)
{
throw new NotImplementedException();
}
}
}
用法:
XmlAttributeOverrides attributeOverrides = XmlAttributeOverrideGenerator<Person>.Get();
var serializer = new XmlSerializer(typeof(Person), attributeOverrides);
我没有足够的声誉来评论 steve16351 的回答。但我觉得我对他的代码做了一点改进。
我的用例涉及通过 Microsoft 提供的 XSD.exe 生成的 XML 架构文件,然后从架构文件生成 class。所以 class 上有架构中的所有 DefaultValue 标签。还创建了许多嵌套类型。因此,为了使我的代码简洁,我修改了 'Generate' 方法以获取 top-level class 及其下方的所有方法的覆盖。然后所有内容都在单个 XmlAttributesOverrides 对象中返回。
static XmlAttributeOverrideGenerator()
{
_overrides = Generate(typeof(T), new XmlAttributeOverrides());
}
private static XmlAttributeOverrides Generate(Type targetType, XmlAttributeOverrides Overrides)
{
foreach (var property in targetType.GetProperties())
{
if (property.CustomAttributes.Any( CA => CA.AttributeType == typeof(DefaultValueAttribute)))
{
XmlAttributes propertyAttributes = new XmlAttributes(new CustomAttribProvider(property, _ignoreAttributes));
Overrides.Add(targetType, property.Name, propertyAttributes);
}
else
{
Type propType = property.PropertyType;
if (propType.IsClass && !propType.IsValueType && !propType.IsEnum && !propType.IsPrimitive && !propType.IsSealed) // Check if this is a custom class
{
//If this is a nested class or other class type, generate its overrides as well
Generate(propType, Overrides);
}
}
}
return Overrides;
}