C# 反序列化以前在序列化时包含值的空字段
我读过许多关于可空字段反序列化的文章,但没有遇到以下情况:C# 反序列化以前在序列化时包含值的空字段,c#,xml,field,deserialization,nullable,C#,Xml,Field,Deserialization,Nullable,我读过许多关于可空字段反序列化的文章,但没有遇到以下情况: 使用包含值的可空字段序列化对象(“nil”属性未添加到节点,因为它包含值) 从xml中的可空字段中删除该值(这是通过客户端处理实现的) 反序列化xml 步骤3引发错误,因为序列化程序没有将可空字段的空值视为空值(因为未指定“nil=true”)。相反,它尝试将值转换为字段的数据类型(例如:Guid),但失败会导致错误消息,该消息因字段的数据类型而异 对于Guid,错误消息为: System.InvalidOperationExc
System.InvalidOperationException: There is an error in XML document ([line number], [column number]). ---> System.FormatException: Unrecognized Guid format.
我应该注意,我们使用的序列化/反序列化方法是使用泛型的框架方法
我正在寻找一个优雅和通用的解决方案。我能想到的唯一可行的通用解决方案是:
public class DummyData
{
public Guid? NullableGuid { get; set; }
}
发送到客户端的Xml
<DummyData>
<NullableGuid>052ec82c-7322-4745-9ac1-20cc4e0f142d</NullableGuid>
</DummyData>
052ec82c-7322-4745-9ac1-20cc4e0f142d
从客户端返回的Xml(错误)
从客户端返回的Xml(所需结果)
这是我提出的解决方案,与我在原始问题中描述的攻击计划非常相似 免责声明:它并不简短,很可能没有涵盖所有反序列化场景,但似乎完成了工作
public static T FromXml<T>(string xml)
{
string convertedXml = AddNilAttributesToNullableTypesWithNullValues(typeof(T), xml);
var reader = new StringReader(convertedXml);
var serializer = new XmlSerializer(typeof (T));
var data = (T) serializer.Deserialize(reader);
reader.Close();
return data;
}
private static string AddNilAttributesToNullableTypesWithNullValues(Type type, string xml)
{
string result;
if (!string.IsNullOrWhiteSpace(xml))
{
XDocument doc = XDocument.Parse(xml);
if (doc.Root != null)
AddNilAttributesToNullableTypesWithNullValues(doc.Root, type);
result = doc.ToString();
}
else
result = xml;
return result;
}
private static void AddNilAttributesToNullableTypesWithNullValues(XElement element, Type type)
{
if (type == null)
throw new ArgumentNullException("type");
if (element == null)
throw new ArgumentNullException("element");
//If this type can be null and it does not have a value, add or update nil attribute
//with a value of true.
if (IsReferenceOrNullableType(type) && string.IsNullOrEmpty(element.Value))
{
XAttribute existingNilAttribute = element.Attributes().FirstOrDefault(a => a.Name.LocalName == NIL_ATTRIBUTE_NAME);
if (existingNilAttribute == null)
element.Add(NilAttribute);
else
existingNilAttribute.SetValue(true);
}
else
{
//Process all of the objects' properties that have a corresponding child element.
foreach (PropertyInfo property in type.GetProperties())
{
string elementName = GetElementNameByPropertyInfo(property);
foreach (XElement childElement in element.Elements().Where(e =>
e.Name.LocalName.Equals(elementName)))
{
AddNilAttributesToNullableTypesWithNullValues(childElement, property.PropertyType);
}
}
//For generic IEnumerable types that have elements that correspond to the enumerated type,
//process the each element.
if (IsGenericEnumerable(type))
{
Type enumeratedType = GetEnumeratedType(type);
if (enumeratedType != null)
{
IEnumerable<XElement> enumeratedElements = element.Elements().Where(e =>
e.Name.LocalName.Equals(enumeratedType.Name));
foreach (XElement enumerableElement in enumeratedElements)
AddNilAttributesToNullableTypesWithNullValues(enumerableElement, enumeratedType);
}
}
}
}
private static string GetElementNameByPropertyInfo(PropertyInfo property)
{
string overrideElementName = property.GetCustomAttributes(true).OfType<XmlElementAttribute>().Select(xmlElementAttribute =>
xmlElementAttribute.ElementName).FirstOrDefault();
return overrideElementName ?? property.Name;
}
private static Type GetEnumeratedType(Type type)
{
Type enumerableType = null;
Type[] types = type.GetGenericArguments();
if (types.Length == 1)
enumerableType = types[0];
return enumerableType;
}
public static bool IsGenericEnumerable(Type type)
{
return type.IsGenericType && type.GetInterfaces().Any(i =>
i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
}
private static bool IsReferenceOrNullableType(Type type)
{
return !type.IsValueType || Nullable.GetUnderlyingType(type) != null;
}
private const string NIL_ATTRIBUTE_NAME = "nil";
private const string XML_SCHEMA_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance";
private static XAttribute NilAttribute
{
get
{
if (_nilAttribute == null)
{
XNamespace xmlSchemaNamespace = XNamespace.Get(XML_SCHEMA_NAMESPACE);
_nilAttribute = new XAttribute(xmlSchemaNamespace + NIL_ATTRIBUTE_NAME, true);
}
return _nilAttribute;
}
}
publicstatict FromXml(字符串xml)
{
string convertedXml=AddNilAttributesToNullableTypesWithNullValues(typeof(T),xml);
var reader=newstringreader(convertedXml);
var serializer=newxmlserializer(typeof(T));
var data=(T)序列化程序。反序列化(读取器);
reader.Close();
返回数据;
}
私有静态字符串AddNilAttributesToNullableTypesWithNullValues(类型类型,字符串xml)
{
字符串结果;
如果(!string.IsNullOrWhiteSpace(xml))
{
XDocument doc=XDocument.Parse(xml);
如果(doc.Root!=null)
AddNilAttributesToNullableTypesWithNullValues(doc.Root,type);
结果=doc.ToString();
}
其他的
结果=xml;
返回结果;
}
私有静态void AddNilAttributesToNullableTypesWithNullValues(XElement元素,类型类型)
{
if(type==null)
抛出新的ArgumentNullException(“类型”);
if(元素==null)
抛出新的ArgumentNullException(“元素”);
//如果此类型可以为null且没有值,请添加或更新nil属性
//值为true。
if(IsReferenceOrNullableType(type)和&string.IsNullOrEmpty(element.Value))
{
XAttribute existingnillattribute=element.Attributes().FirstOrDefault(a=>a.Name.LocalName==NIL\u ATTRIBUTE\u Name);
如果(existingNilAttribute==null)
元素。添加(NilAttribute);
其他的
existingNilAttribute.SetValue(true);
}
其他的
{
//处理具有相应子元素的所有对象属性。
foreach(类型.GetProperties()中的PropertyInfo属性)
{
字符串elementName=GetElementNameByPropertyInfo(属性);
foreach(element.Elements()中的XElement childElement,其中(e=>
e、 Name.LocalName.Equals(elementName)))
{
AddNilAttributesToNullableTypesWithNullValues(childElement,property.PropertyType);
}
}
//对于具有与枚举类型对应的元素的泛型IEnumerable类型,
//对每个元素进行处理。
if(IsGenericEnumerable(类型))
{
类型enumeratedType=GetEnumeratedType(类型);
if(enumeratedType!=null)
{
IEnumerable enumeratedElements=element.Elements()。其中(e=>
e、 Name.LocalName.Equals(enumeratedType.Name));
foreach(EnumeratedElement中的XElement EnumeratableElement)
AddNilAttributesToNullableTypesWithNullValues(EnumeratableElement,enumeratedType);
}
}
}
}
私有静态字符串GetElementNameByPropertyInfo(PropertyInfo属性)
{
string overrideElementName=property.GetCustomAttributes(true).OfType().Select(xmlElementAttribute=>
xmlElementAttribute.ElementName).FirstOrDefault();
返回overrideElementName??property.Name;
}
私有静态类型GetEnumeratedType(类型类型)
{
类型enumerableType=null;
Type[]types=Type.GetGenericArguments();
if(types.Length==1)
enumerableType=类型[0];
返回enumerableType;
}
公共静态bool IsGenericEnumerable(类型)
{
返回type.IsGenericType&&type.GetInterfaces().Any(i=>
i、 IsGenericType&&i.GetGenericTypeDefinition()==typeof(IEnumerable));
}
私有静态bool IsReferenceOrNullableType(类型)
{
return!type.IsValueType | | Nullable.GetUnderlyingType(type)!=null;
}
<DummyData>
<NullableGuid p2:nil="true" xmlns:p2="http://www.w3.org/2001/XMLSchema-instance"></NullableGuid>
</DummyData>
public static T FromXml<T>(string xml)
{
string convertedXml = AddNilAttributesToNullableTypesWithNullValues(typeof(T), xml);
var reader = new StringReader(convertedXml);
var serializer = new XmlSerializer(typeof (T));
var data = (T) serializer.Deserialize(reader);
reader.Close();
return data;
}
private static string AddNilAttributesToNullableTypesWithNullValues(Type type, string xml)
{
string result;
if (!string.IsNullOrWhiteSpace(xml))
{
XDocument doc = XDocument.Parse(xml);
if (doc.Root != null)
AddNilAttributesToNullableTypesWithNullValues(doc.Root, type);
result = doc.ToString();
}
else
result = xml;
return result;
}
private static void AddNilAttributesToNullableTypesWithNullValues(XElement element, Type type)
{
if (type == null)
throw new ArgumentNullException("type");
if (element == null)
throw new ArgumentNullException("element");
//If this type can be null and it does not have a value, add or update nil attribute
//with a value of true.
if (IsReferenceOrNullableType(type) && string.IsNullOrEmpty(element.Value))
{
XAttribute existingNilAttribute = element.Attributes().FirstOrDefault(a => a.Name.LocalName == NIL_ATTRIBUTE_NAME);
if (existingNilAttribute == null)
element.Add(NilAttribute);
else
existingNilAttribute.SetValue(true);
}
else
{
//Process all of the objects' properties that have a corresponding child element.
foreach (PropertyInfo property in type.GetProperties())
{
string elementName = GetElementNameByPropertyInfo(property);
foreach (XElement childElement in element.Elements().Where(e =>
e.Name.LocalName.Equals(elementName)))
{
AddNilAttributesToNullableTypesWithNullValues(childElement, property.PropertyType);
}
}
//For generic IEnumerable types that have elements that correspond to the enumerated type,
//process the each element.
if (IsGenericEnumerable(type))
{
Type enumeratedType = GetEnumeratedType(type);
if (enumeratedType != null)
{
IEnumerable<XElement> enumeratedElements = element.Elements().Where(e =>
e.Name.LocalName.Equals(enumeratedType.Name));
foreach (XElement enumerableElement in enumeratedElements)
AddNilAttributesToNullableTypesWithNullValues(enumerableElement, enumeratedType);
}
}
}
}
private static string GetElementNameByPropertyInfo(PropertyInfo property)
{
string overrideElementName = property.GetCustomAttributes(true).OfType<XmlElementAttribute>().Select(xmlElementAttribute =>
xmlElementAttribute.ElementName).FirstOrDefault();
return overrideElementName ?? property.Name;
}
private static Type GetEnumeratedType(Type type)
{
Type enumerableType = null;
Type[] types = type.GetGenericArguments();
if (types.Length == 1)
enumerableType = types[0];
return enumerableType;
}
public static bool IsGenericEnumerable(Type type)
{
return type.IsGenericType && type.GetInterfaces().Any(i =>
i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
}
private static bool IsReferenceOrNullableType(Type type)
{
return !type.IsValueType || Nullable.GetUnderlyingType(type) != null;
}
private const string NIL_ATTRIBUTE_NAME = "nil";
private const string XML_SCHEMA_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance";
private static XAttribute NilAttribute
{
get
{
if (_nilAttribute == null)
{
XNamespace xmlSchemaNamespace = XNamespace.Get(XML_SCHEMA_NAMESPACE);
_nilAttribute = new XAttribute(xmlSchemaNamespace + NIL_ATTRIBUTE_NAME, true);
}
return _nilAttribute;
}
}