3

null許容フィールドの逆シリアル化に関する多くの投稿を読みましたが、次のシナリオに遭遇していません。

  1. 値を含むnull許容フィールドを使用してオブジェクトをシリアル化します(「nil」属性には値が含まれているため、ノードに追加されません)。
  2. xmlのnull許容フィールドから値を削除します(これはクライアント側の処理を介して行われます)。
  3. xmlを逆シリアル化します。

ステップ3は、シリアライザーがnull許容フィールドの空の値をnull値として処理しないため(「nil = true」が指定されていないため)、エラーをスローします。代わりに、値をフィールドのデータ型(例:GUID)に変換しようとしますが、失敗すると、フィールドのデータ型によって異なるエラーメッセージが表示されます。

Guidの場合、エラーメッセージは次のとおりです。

    System.InvalidOperationException: There is an error in XML document ([line number], [column number]). ---> System.FormatException: Unrecognized Guid format.

私たちが使用するシリアル化/逆シリアル化メソッドは、ジェネリックを使用するフレームワークメソッドであることに注意してください。

エレガントで一般的なソリューションを探しています。私が考えることができる唯一の実行可能な一般的な解決策は次のとおりです。

  1. xmlをXDocumentに変換します。
  2. 参照型であるオブジェクトのすべてのプロパティを取得するには、(必要とは言えない)リフレクションを使用します。
  3. #2のリストで名前が見つかり、値が空のすべてのノードに「nil=true」属性を追加します。
  4. 再帰を使用して、#2の各参照型を処理します。

注:空の値を持つすべてのノードに「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>
4

1 に答える 1

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;
     }
  }
于 2012-10-11T11:54:59.840 に答える