2015-03-06 4 views
9

में एक अंतरिक्ष परिणाम के साथ एक फ्लैग किए गए enum डी-serializing जब डी-serializing एक फ्लैग किए गए enum है कि एक जगह एक SerializationException फेंक दिया जाता है जिसमें एक मूल्य के साथ एक EnumMemberAttribute साथ सजाया गया है। मूल्य में स्थान को विभाजक के रूप में माना जाता है।SerializationException

वहाँ विभाजक बदलने के लिए या उद्धरण में मूल्यों डाल करने के लिए कोई तरीका है? या यहां तक ​​कि एक और आसान समाधान है?

विकल्प मैं पहले से ही विचार कर रहा हैं:

  • इस enum प्रकार
  • अंडरस्कोर से रिक्त स्थान की जगह की सूची के साथ चिह्नित enum की जगह
  • यह एक WCF सेवा में प्रयोग किया जाता है, और मैं पता है कि कुछ लोगों द्वारा datacontracts में enums एक बुरी बात पर विचार कर रहे हैं। तो मैं भी enum को सभी को खोने के बारे में सोच रहा हूं।

लेकिन मुझे सच में लगता है कि यह कुछ कॉन्फ़िगर करने योग्य या कुछ अन्य लोगों को पहले से हल किया जाना चाहिए। लेकिन मुझे कुछ भी नहीं मिला।

मैं एक साधारण इकाई परीक्षण करने के लिए नीचे समस्या उबला हुआ है। में परिणाम नीचे कोड:

संदेश = अमान्य enum मान 'टेस्ट' प्रकार 'UnitTests.TestEnum' में deserialized नहीं किया जा सकता। सुनिश्चित करें कि आवश्यक enum मान मौजूद हैं और यदि प्रकार DataContractAttribute विशेषता है EnumMemberAttribute विशेषता के साथ चिह्नित हैं। स्रोत = System.Runtime.Serialization

using System; 
using System.IO; 
using System.Runtime.Serialization; 
using System.Xml; 
using FluentAssertions; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 

namespace UnitTests 
{ 
    [TestClass] 
    public class EnumSerizalizationTests 
    { 
     [TestMethod] 
     public void SerializingAndDesrializingAFlaggedEnumShouldResultInSameEnumValues() 
     { 
      //Arrange 
      var orgObject = new TestClass { Value = TestEnum.TestValue1 | TestEnum.TestValue2 }; 
      //Act 
      var temp = DataContractSerializeObject(orgObject); 
      var newObject = DataContractDeSerializeObject<TestClass>(temp); 

      //Assert 
      newObject.ShouldBeEquivalentTo(orgObject, "Roundtripping serialization should result in same value"); 
     } 

     public string DataContractSerializeObject<T>(T objectToSerialize) 
     { 
      using (var output = new StringWriter()) 
      { 
       using (var writer = new XmlTextWriter(output) {Formatting = Formatting.Indented}) 
       { 
        new DataContractSerializer(typeof (T)).WriteObject(writer, objectToSerialize); 
        return output.GetStringBuilder().ToString(); 
       } 
      } 
     } 

     public T DataContractDeSerializeObject<T>(string stringToDeSerialize) 
     { 
      DataContractSerializer ser = new DataContractSerializer(typeof(T)); 
      T result; 
      using (StringReader stringReader = new StringReader(stringToDeSerialize)) 
      { 
       using (XmlReader xmlReader = XmlReader.Create(stringReader)) 
       { 
        result = (T)ser.ReadObject(xmlReader); 
       } 
      } 
      return result; 
     } 

    } 

    [DataContract] 
    [KnownType(typeof(TestEnum))] 
    public class TestClass 
    { 
     [DataMember] 
     public TestEnum Value { get; set; } 
    } 

    [Flags] 
    [DataContract] 
    public enum TestEnum 
    { 
     [EnumMember(Value = "Test value one")] 
     TestValue1 = 1, 
     [EnumMember(Value = "Test value two")] 
     TestValue2 = 2, 
     [EnumMember] 
     TestValue3 = 4, 
     [EnumMember] 
     TestValue4 = 8, 
    } 


} 
+2

मैं यह नहीं कहूंगा कि "डेटाकंट्रैक्ट्स में enums एक बुरी चीज है" लेकिन मेरे लिए "enums में रिक्त स्थान का उपयोग करना एक बुरी चीज है" ;-) – fixagon

+0

सहमत है :) मुझे कहीं भी रिक्त स्थान पसंद नहीं हैं, लेकिन यह मेरी पसंद नहीं है । यदि मैं आपको उन मूल्यों को दिखाऊंगा जो वास्तविक enums में जाने की आवश्यकता है तो आप रोना शुरू कर देंगे। – KeesDijk

+0

उस पर भी देखें: http://stackoverflow.com/questions/1415140/can-my-enums-have-friendly- नाम लेकिन वास्तव में आपकी स्थिति में सहायता नहीं करता है। अन्यथा आप इंट के रूप में enum क्रमबद्ध कर सकते हैं। लेकिन यह डेटाकंट्रैक्ट – fixagon

उत्तर

5

आप मूल्यों में स्थान का उपयोग नहीं कर सकते क्योंकि DataContractSerializer इसे इस्तेमाल करता है और यह हार्डकोडेड है। source और post देखें। लेकिन यदि आप वास्तव में शब्दों के बीच स्थान का उपयोग करना चाहते हैं, तो सूचीबद्ध समाधानों में से एक का उपयोग करें:

पहला तरीका। मूल्यों में तीन-प्रति-एम स्पेस जैसे अन्य व्हाइटस्पेस वर्णों का उपयोग करें। लेकिन आपको एक और समस्या होगी - मूल्यों के बीच कोई दृश्य विभाजक नहीं है।

<TestClass xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication"> 
    <Value>Test value one Test value two</Value> 
</TestClass> 

दूसरा तरीकाIDataContractSurrogate उपयोग करने के लिए है। इस प्रकार नीचे सूचीबद्ध एक्सएमएल का उत्पादन होगा:

<TestClass xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication"> 
    <Value i:type="EnumValueOfTestEnum9cBcd6LT">Test value one, Test value two</Value> 
</TestClass> 

यह कैसे काम करता है? हम केवल क्रमिकरण की प्रक्रिया में हमारी गणना को लपेटेंगे और deserialization के मामले में unwrap। आदेश है कि हम का उपयोग करना चाहिए करने के लिए IDataContractSurrogate:

new DataContractSerializerSettings() 
{ 
    DataContractSurrogate = new EnumSurrogate(), 
    KnownTypes = new Type[] { typeof(EnumValue<TestEnum>) } 
}; 

public class EnumSurrogate : IDataContractSurrogate 
{ 
    #region IDataContractSurrogate Members 

    public object GetCustomDataToExport(Type clrType, Type dataContractType) 
    { 
     return null; 
    } 

    public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType) 
    { 
     return null; 
    } 

    public Type GetDataContractType(Type type) 
    { 
     return type; 
    } 

    public object GetDeserializedObject(object obj, Type targetType) 
    { 
     IEnumValue enumValue = obj as IEnumValue; 

     if (enumValue!= null) 
     { return enumValue.Value; } 

     return obj; 
    } 

    public void GetKnownCustomDataTypes(Collection<Type> customDataTypes) 
    { 
    } 

    public object GetObjectToSerialize(object obj, Type targetType) 
    { 
     if (obj != null) 
     { 
      Type type = obj.GetType(); 

      if (type.IsEnum && Attribute.IsDefined(type, typeof(FlagsAttribute))) 
      { return Activator.CreateInstance(typeof(EnumValue<>).MakeGenericType(type), obj); } 
     } 

     return obj; 
    } 

    public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData) 
    { 
     return null; 
    } 

    public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit) 
    { 
     return null; 
    } 

    #endregion 
} 

public interface IEnumValue : IXmlSerializable 
{ 
    object Value { get; } 
} 

[Serializable] 
public class EnumValue<T> : IEnumValue 
    where T : struct 
{ 
    #region Fields 

    private Enum value; 
    private static Type enumType; 
    private static long[] values; 
    private static string[] names; 
    private static bool isULong; 

    #endregion 

    #region Constructors 

    static EnumValue() 
    { 
     enumType = typeof(T); 

     if (!enumType.IsEnum) 
     { throw new InvalidOperationException(); } 

     FieldInfo[] fieldInfos = enumType.GetFields(BindingFlags.Static | BindingFlags.Public); 

     values = new long[fieldInfos.Length]; 
     names = new string[fieldInfos.Length]; 
     isULong = Enum.GetUnderlyingType(enumType) == typeof(ulong); 

     for (int i = 0; i < fieldInfos.Length; i++) 
     { 
      FieldInfo fieldInfo = fieldInfos[i]; 
      EnumMemberAttribute enumMemberAttribute = (EnumMemberAttribute)fieldInfo 
       .GetCustomAttributes(typeof(EnumMemberAttribute), false) 
       .FirstOrDefault(); 
      IConvertible value = (IConvertible)fieldInfo.GetValue(null); 

      values[i] = (isULong) 
       ? (long)value.ToUInt64(null) 
       : value.ToInt64(null); 
      names[i] = (enumMemberAttribute == null || string.IsNullOrEmpty(enumMemberAttribute.Value)) 
       ? fieldInfo.Name 
       : enumMemberAttribute.Value; 
     } 
    } 

    public EnumValue() 
    { 
    } 

    public EnumValue(Enum value) 
    { 
     this.value = value; 
    } 

    #endregion 

    #region IXmlSerializable Members 

    public XmlSchema GetSchema() 
    { 
     return null; 
    } 

    public void ReadXml(XmlReader reader) 
    { 
     string stringValue = reader.ReadElementContentAsString(); 

     long longValue = 0; 
     int i = 0; 

     // Skip initial spaces 
     for (; i < stringValue.Length && stringValue[i] == ' '; i++) ; 

     // Read comma-delimited values 
     int startIndex = i; 
     int nonSpaceIndex = i; 
     int count = 0; 

     for (; i < stringValue.Length; i++) 
     { 
      if (stringValue[i] == ',') 
      { 
       count = nonSpaceIndex - startIndex + 1; 

       if (count > 1) 
       { longValue |= ReadEnumValue(stringValue, startIndex, count); } 

       nonSpaceIndex = ++i; 

       // Skip spaces 
       for (; i < stringValue.Length && stringValue[i] == ' '; i++) ; 

       startIndex = i; 

       if (i == stringValue.Length) 
       { break; } 
      } 
      else 
      { 
       if (stringValue[i] != ' ') 
       { nonSpaceIndex = i; } 
      } 
     } 

     count = nonSpaceIndex - startIndex + 1; 

     if (count > 1) 
      longValue |= ReadEnumValue(stringValue, startIndex, count); 

     value = (isULong) 
      ? (Enum)Enum.ToObject(enumType, (ulong)longValue) 
      : (Enum)Enum.ToObject(enumType, longValue); 
    } 

    public void WriteXml(XmlWriter writer) 
    { 
     long longValue = (isULong) 
      ? (long)((IConvertible)value).ToUInt64(null) 
      : ((IConvertible)value).ToInt64(null); 

     int zeroIndex = -1; 
     bool noneWritten = true; 

     for (int i = 0; i < values.Length; i++) 
     { 
      long current = values[i]; 

      if (current == 0) 
      { 
       zeroIndex = i; 
       continue; 
      } 

      if (longValue == 0) 
      { break; } 

      if ((current & longValue) == current) 
      { 
       if (noneWritten) 
       { noneWritten = false; } 
       else 
       { writer.WriteString(","); } 

       writer.WriteString(names[i]); 
       longValue &= ~current; 
      } 
     } 

     if (longValue != 0) 
     { throw new InvalidOperationException(); } 

     if (noneWritten && zeroIndex >= 0) 
     { writer.WriteString(names[zeroIndex]); } 
    } 

    #endregion 

    #region IEnumValue Members 

    public object Value 
    { 
     get { return value; } 
    } 

    #endregion 

    #region Private Methods 

    private static long ReadEnumValue(string value, int index, int count) 
    { 
     for (int i = 0; i < names.Length; i++) 
     { 
      string name = names[i]; 

      if (count == name.Length && string.CompareOrdinal(value, index, name, 0, count) == 0) 
      { return values[i]; } 
     } 

     throw new InvalidOperationException(); 
    } 

    #endregion 
} 

तीसरा रास्ता वर्ग, गतिशील रूप से फेंकना आधार वर्ग Enum गुण में चिह्नित किया है, तो उन्हें string गुणों के साथ की जगह और के रूप में उत्पन्न वर्ग के उदाहरण का उपयोग करना है किराए की कोख।

+0

आपके लिए विस्तृत उत्तर के लिए धन्यवाद! हार्डकोडेड सेपरेटर को देखते हुए मुझे विश्वास दिलाता है कि मुझे एक साधारण विकल्प नहीं मिला है। आपका दूसरा विकल्प दिलचस्प लग रहा है, मुझे अभी तक इसका उपयोग करने के सभी प्रभाव नहीं दिख रहे हैं। मैं इसे थोड़ा बेहतर देखूंगा और देख सकता हूं कि यह प्रयास के लायक है या नहीं। – KeesDijk

+0

मैंने 'EnumValue ' वर्ग कार्यान्वयन पूरा किया। और अब आप इसका इस्तेमाल कर सकते हैं। –

संबंधित मुद्दे