2012-11-30 11 views
6

我想創建一個通用函數,當給定一個枚舉類型時,將返回一個對象,當通過WebApi序列化時將提供漂亮的輸出爲XML/Json。在Web API中序列化一個動態類型

此方法在串行化爲JSON時效果很好,但我無法使用XML進行操作。如果我使用XmlSerializer或DataContractSerializer手動序列化返回的對象,我會按預期得到結果。當的WebAPI本身試圖序列化,另一方面從一個HttpRequest,我得到像下面的錯誤:

System.Runtime.Serialization.SerializationException

類型「優先級」的數據合同名稱 「優先:http://schemas.datacontract.org/2004/07/'不是預期的。 考慮使用DataContractResolver或將靜態添加到已知類型列表中的任何類型的未知類型 - 例如,使用 KnownTypeAttribute屬性或將它們添加到傳遞給DataContractSerializer的已知 類型的列表中。

我使用GlobalConfiguration.Configuration.Formatters.XmlFormatter.SetSerializer來設置生成的類型,我知道,從設置斷點作品串行試過,但它只是似乎忽略它,並拋出了同樣的異常。枚舉將由整數支持,並保證每個條目都有唯一的值。這裏是我用來生成類型並返回它的一個實例的代碼。

public object GetSerializableEnumProxy(Type enumType) { 

    if (enumType == null) { 
     throw new ArgumentNullException("enumType"); 
    } 

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

    AssemblyName assemblyName = new AssemblyName("DataBuilderAssembly"); 
    AssemblyBuilder assemBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); 
    ModuleBuilder moduleBuilder = assemBuilder.DefineDynamicModule("DataBuilderModule"); 
    TypeBuilder typeBuilder = moduleBuilder.DefineType(enumType.Name, TypeAttributes.Class | TypeAttributes.Public); 

    // Add the [DataContract] attribute to our generated type 
    typeBuilder.SetCustomAttribute(
     new CustomAttributeBuilder(typeof(DataContractAttribute).GetConstructor(Type.EmptyTypes), new object[] {}) 
    ); 

    CustomAttributeBuilder dataMemberAttributeBuilder = new CustomAttributeBuilder(
     typeof(DataMemberAttribute).GetConstructor(Type.EmptyTypes), new object[] {} 
    ); 

    // For each name in the enum, define a corresponding public int field 
    // with the [DataMember] attribute 
    foreach (var value in Enum.GetValues(enumType).Cast<int>()) { 
     var name = Enum.GetName(enumType, value); 

     var fb = typeBuilder.DefineField(name, typeof(int), FieldAttributes.Public); 

     // Add the [DataMember] attribute to the field 
     fb.SetCustomAttribute(dataMemberAttributeBuilder); 

     // Set the value of our field to be the corresponding value from the Enum 
     fb.SetConstant(value); 
    }  

    // Return an instance of our generated type 
    return Activator.CreateInstance(typeBuilder.CreateType()); 
} 

網絡API控制器的方法:

private static IEnumerable<Type> RetrievableEnums = new Type[] { 
    typeof(Priority), typeof(Status) 
}; 

[GET("enum/{enumName}")] 
public HttpResponseMessage GetEnumInformation(string enumName) { 

    Type enumType = RetrievableEnums.SingleOrDefault(type => 
     String.Equals(type.Name, enumName, StringComparison.InvariantCultureIgnoreCase)); 

    if (enumType == null) { 
     return Request.CreateErrorResponse(HttpStatusCode.NotFound, "The requested enum could not be retrieved"); 
    } 

    return Request.CreateResponse(HttpStatusCode.OK, GetSerializableEnumProxy(enumType)); 
} 

任何想法?

+0

您是否可以包含一個web api方法,在返回XML時重現此錯誤?我想我知道問題是什麼,但我需要看看你是如何將這個枚舉作爲內容返回的。 –

+0

@AndrasZoltan編輯我的原始問題與示例Web API方法 – dherman

+0

正如我懷疑 - 將對象作爲一個'對象'傳遞給內容 - 從接受我可以看到,我建議工作的解決方案:) –

回答

8

我相信這最終是因爲你發送的枚舉值爲object--而且與Json格式化程序不同,Web API的xml格式化程序(使用DataContractSerializer)使用(實際上)編譯時類型的值序列化,而不是運行時類型。

因此,您必須始終確保您嘗試序列化的任何基類的派生類型都被添加到基礎序列化器的已知類型中。在這種情況下,你有動態枚舉(當然是object)。

在它的面前,似乎SetSerializer(type, serializer)方法應該工作,但是,我敢打賭你與動態類型作爲第一個參數調用它 - 如果你發送枚舉這將無法正常工作作爲object - 因爲它是串行器,XmlRequestFormatter將使用。

這是一個衆所周知的問題 - 一個which I've reported as an issue on codeplex(有一個很好的例子,它演示了一個更簡單的情況下的問題)。

該問題還包括一些屬性的C#代碼和替代XmlMediaTypeFormatter(稱爲XmlMediaTypeFormatterEx),它提供瞭解決此問題的一種解決方案 - 它使用聲明性的每操作方法。用一個代碼替換XmlMediaTypeFormatter - 使用像這樣(注意這個代碼處理那裏已經沒有定義XML格式的情況下 - 或許有些無意義):對API方法

var configuration = GlobalConfiguration.Configuration; 
var origXmlFormatter = configuration.Formatters.OfType<XmlMediaTypeFormatter>() 
         .SingleOrDefault(); 

XmlMediaTypeFormatterEx exXmlFormatter = new XmlMediaTypeFormatterEx(origXmlFormatter); 

if (origXmlFormatter != null) 
{ 
    configuration.Formatters.Insert(
     configuration.Formatters.IndexOf(origXmlFormatter), exXmlFormatter); 
    configuration.Formatters.Remove(origXmlFormatter); 
} 
else 
    configuration.Formatters.Add(exXmlFormatter); 

而現在,你想回到這個你這個裝飾它動態枚舉:

[XmlUseReturnedUnstanceType] 
public object Get() 
{ 

} 

現在,你從Get方法返回任何類型,在自定義格式踢和使用DataContractSerializer專門爲運行時類型,不是object

這不處理基數的枚舉或字典 - 它變得非常複雜 - 但對於基本的單實例返回值,它工作正常。