我已阅读很多有关可空字段反序列化的帖子,但未遇到以下情况:
步骤3抛出错误,因为序列化程序不会将可空字段的空值视为空值(因为未指定“nil = true”)。它反而尝试将值转换为字段的数据类型(例如:Guid),这会失败,从而导致错误消息因字段的数据类型而异。
如果是Guid,则错误消息为:
System.InvalidOperationException: There is an error in XML document ([line number], [column number]). ---> System.FormatException: Unrecognized Guid format.
我应该注意,我们使用的序列化/反序列化方法是使用泛型的框架方法。
我正在寻找一种优雅而通用的解决方案。我能想到的唯一可行的通用解决方案如下:
注意:简单地将“nil = true”添加到具有空值的所有节点将不起作用,因为序列化程序将为不能为null的值类型引发错误。
[编辑]代码示例:
示例数据类
public class DummyData
{
public Guid? NullableGuid { get; set; }
}
Xml发送到客户端
<DummyData>
<NullableGuid>052ec82c-7322-4745-9ac1-20cc4e0f142d</NullableGuid>
</DummyData>
从客户端返回的Xml(错误)
<DummyData>
<NullableGuid></NullableGuid>
</DummyData>
从客户端返回的Xml(期望的结果)
<DummyData>
<NullableGuid p2:nil="true" xmlns:p2="http://www.w3.org/2001/XMLSchema-instance"></NullableGuid>
</DummyData>
答案 0 :(得分:0)
我提出的解决方案非常类似于原始问题中描述的攻击计划。
免责声明:它不短,很可能不会涵盖所有反序列化方案,但似乎可以完成工作。
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;
}
}