2010-05-17 44 views
4

試想一下,你有下面的類如何定義哪些類是如果一個接口屬性反序列化在一個類中?

[DataContract] 
public class NamedList 
{ 
    [DataMember] 
    public string Name { get; set; } 

    [DataMember] 
    public IList<string> Items { get; private set; } 

    public DumpList(string name) 
    { 
     Name = name; 
     Items = new List<string>(); 
    } 
} 

如果您序列化到文件這一點,這是很容易,導致IList背後的具體類是已知的,可以被序列化。

但是,如果您嘗試將此文件反序列化回內存會發生什麼?
它工作沒有任何直接的錯誤發生。

如果您嘗試在列表中添加或刪除某些內容,則會出現問題。在這種情況下,你會得到一個例外。此異常的根源來自反序列化對象用作IList數組的具體實現的情況。

爲了避免這個問題,在這個簡單的例子很容易。只是序列化的具體後備存儲,而不是公共財產,並在構造函數中的變化:

[DataMember(Name = "Items")] 
private List<string> _Items; 

public IList<string> Items 
{ 
    get 
    { 
     return _Items; 
    } 
} 

public DumpList(string name) 
{ 
    Name = name; 
    _Items = new List<string>(); 
} 

但更有趣的問題是:

  • 爲什麼選擇解串器的陣列式的具體實施IList接口?
  • 是否可以更改每個接口的類別設置?
  • 如果我有一個自定義的接口和這個接口的幾個實現,是否可以告訴Deserializer應該爲給定接口採用哪個具體類?

回答

1

如果使用存儲類型信息和序列化對象的NetDataContractSerializer,則應解決問題。但是,它同時降低了與非.NET客戶端的互操作性。

+0

其實我不想在我的xml文件中存儲類型信息。但似乎MS已經實現了一些機制來採取特定類型,如果沒有給出(像只有一個接口)。問題是他們如何解決它,你能以某種方式配置這種機制(可能通過我的接口/類上的Attribute或通過配置DataContractSerializer實例) – Oliver 2010-05-17 11:47:33

+0

集合類型的Data Contract序列化行爲的完整文檔在這裏在MSDN上:http://msdn.microsoft.com/en-us/library/aa347850.aspx 讀取,恐怕你無法控制爲集合接口實例化的類型。 – Lucero 2010-05-17 15:43:53

+0

我對你提到的頁面做了一個粗略的閱讀,發現你的意思是:「類型的選擇是內置於WCF」。所以這感覺非常不舒服。我讓這個問題開放一段時間,也許有人有另一個想法。但我認爲不會有更好的答案。 *嘆息* – Oliver 2010-05-18 06:28:30

3

使用DataContractSurrogate進行反序列化,您可以使用DataContractSurrogate解決這個,它用List替換IList。

public class CustomDataContractSurrogate : IDataContractSurrogate 
{ 
    // The only function you should care about here. The rest don't do anything, just default behavior. 
    public Type GetDataContractType(Type type) 
    { 
     if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(ICollection<>))) 
     { 
      return (typeof(List<>).MakeGenericType(type.GetGenericArguments().Single())); 
     } 
     return type; 
    } 

    public object GetObjectToSerialize(object obj, Type targetType) 
    { 
     return obj; 
    } 

    public object GetDeserializedObject(object obj, Type targetType) 
    { 
     return obj; 
    } 

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

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

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

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

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

基本上就是這樣,你只需要與代理創建DataContractSerializer的實例,並用它來反序列化(系列化都不會有問題),例如:

var serializer = new DataContractSerializer(type, new Type[]{}, Int32.MaxValue, false, true, new CustomDataContractSurrogate()); 

或其任何其他構造函數採用代理。

或者,(作爲獎金答案)如果你使用的應用程序/ web.config中定義的服務工作,你可以定義創建與上述代理數據合同串行定製行爲:

public class CustomDataContractSerializerBehavior : DataContractSerializerOperationBehavior 
{ 
    public CustomDataContractSerializerBehavior(OperationDescription operation) 
     : base(operation) 
    { 
    } 

    public CustomDataContractSerializerBehavior(OperationDescription operation, DataContractFormatAttribute dataContractFormatAttribute) 
     : base(operation, dataContractFormatAttribute) 
    { 
    } 

    public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, 
     IList<Type> knownTypes) 
    { 
     return new DataContractSerializer(type, knownTypes, Int32.MaxValue, false, true, new CustomDataContractSurrogate()); 
    } 

    public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, 
     XmlDictionaryString ns, IList<Type> knownTypes) 
    { 
     return new DataContractSerializer(type, knownTypes, Int32.MaxValue, false, true, new CustomDataContractSurrogate()); 
    } 

} 

最後您可以使用此行爲:

public static IMyDataServiceContract CreateService() 
{ 
    var factory = new ChannelFactory<IMyDataServiceContract>("MyServiceName"); 
    SetDataContractSerializerBehavior(factory.Endpoint.Contract); 
    return factory.CreateChannel(); 
} 

private static void SetDataContractSerializerBehavior(ContractDescription contractDescription) 
{ 
    foreach (OperationDescription operation in contractDescription.Operations) 
    { 
     ReplaceDataContractSerializerOperationBehavior(operation); 
    } 
} 

private static void ReplaceDataContractSerializerOperationBehavior(OperationDescription description) 
{ 
    DataContractSerializerOperationBehavior dcsOperationBehavior = 
    description.Behaviors.Find<DataContractSerializerOperationBehavior>(); 

    if (dcsOperationBehavior != null) 
    { 
     description.Behaviors.Remove(dcsOperationBehavior); 
     description.Behaviors.Add(new CustomDataContractSerializerBehavior(description)); 
    } 
} 

來完成這項工作,調用上面CreateSe在某處創建頻道。

+0

看起來很有希望。下次我需要這種東西,我會測試你的解決方案。所以不要懷疑你是否在某個閃亮的日子獲得了認可的標誌。 ;-) – Oliver 2011-04-04 09:47:40

相關問題