Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/272.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 反序列化以前在序列化时包含值的空字段_C#_Xml_Field_Deserialization_Nullable - Fatal编程技术网

C# 反序列化以前在序列化时包含值的空字段

C# 反序列化以前在序列化时包含值的空字段,c#,xml,field,deserialization,nullable,C#,Xml,Field,Deserialization,Nullable,我读过许多关于可空字段反序列化的文章,但没有遇到以下情况: 使用包含值的可空字段序列化对象(“nil”属性未添加到节点,因为它包含值) 从xml中的可空字段中删除该值(这是通过客户端处理实现的) 反序列化xml 步骤3引发错误,因为序列化程序没有将可空字段的空值视为空值(因为未指定“nil=true”)。相反,它尝试将值转换为字段的数据类型(例如:Guid),但失败会导致错误消息,该消息因字段的数据类型而异 对于Guid,错误消息为: System.InvalidOperationExc

我读过许多关于可空字段反序列化的文章,但没有遇到以下情况:

  • 使用包含值的可空字段序列化对象(“nil”属性未添加到节点,因为它包含值)
  • 从xml中的可空字段中删除该值(这是通过客户端处理实现的)
  • 反序列化xml
  • 步骤3引发错误,因为序列化程序没有将可空字段的空值视为空值(因为未指定“nil=true”)。相反,它尝试将值转换为字段的数据类型(例如:Guid),但失败会导致错误消息,该消息因字段的数据类型而异

    对于Guid,错误消息为:

        System.InvalidOperationException: There is an error in XML document ([line number], [column number]). ---> System.FormatException: Unrecognized Guid format.
    
    我应该注意,我们使用的序列化/反序列化方法是使用泛型的框架方法

    我正在寻找一个优雅和通用的解决方案。我能想到的唯一可行的通用解决方案是:

  • 将xml转换为XDocument
  • 使用(低于所需的)反射来获取对象的所有引用类型属性
  • 将“nil=true”属性添加到从#2中的列表中找到其名称并具有空值的所有节点
  • 使用递归处理#2中的每个引用类型
  • 注意:简单地将“nil=true”添加到所有具有空值的节点将不起作用,因为序列化程序将为不能为null的值类型抛出错误

    [编辑]代码示例:

    样本数据类

        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;
         }
      }