2017-07-18 16 views
0

我有一些樹節點結構的類。它具有兒童屬性,只讀集合類型用於隱藏直接更改子項和添加子控件的AddChild(...)方法。如何使用自定義添加方法進行收集反序列化過程?

class TreeNode { 
    List<TreeNode> _children = new List<TreeNode>(); 
    public IReadOnlyList<TreeNode> Children => children; 
    public string Name { get; set; } // some other filed 
    public void AddChild(TreeNode node){ 
     // ... some code 
     _children.Add(node); 
    } 
} 

我需要爲我的課程提供反序列化。我想:

[Serializable] 
[XmlRoot(ElementName = "node")] 
class TreeNode { 
    List<TreeNode> _children = new List<TreeNode>(); 

    [XmlElement(ElementName = "node")] 
    public IReadOnlyList<TreeNode> Children => children; 

    [XmlAttribute(DataType = "string", AttributeName = "name")] 
    public string Name { get; set; } // some other filed 

    public void AddChild(TreeNode node){ 
     // ... some code 
     _children.Add(node); 
    } 

    public static TreeNode Deserialize(Stream stream) { 
     var serializer = new XmlSerializer(typeof(TreeNode)); 
     var obj = serializer.Deserialize(stream); 
     var tree = (TreeNode)obj; 
     return tree; 
    } 
} 

當然,這並不工作,因爲IReadOnlyList沒有Add方法。

是否可以將AddChild綁定到反序列化過程?如果'是' - 如何?

如何提供具有反序列化能力的相同封裝級別?

+0

任何特別的爲什麼它是一個'ReadOnlyList'?爲什麼不把它變成一個私人列表'然後公開可以添加到它的'AddChild'方法? – JonE

+0

你應該看看'ISerializable'接口。 https://msdn.microsoft.com/en-us/library/system.runtime.serialization.iserializable%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396 –

+1

@CihanYakar'XmlSerializer'不關心關於'ISerializable'和'IXmlSerializable'很難正確實現 –

回答

0

如果這是XmlSerializer,那麼:不,你不能這樣做,除非實現完全IXmlSerializable,這是非常正確地做,並且擊敗首先使用XmlSerializer的全部目的。

如果數據不是很大,那麼我對錶單「我現有的對象模型與我選擇的序列化程序不兼容」的任何問題的默認答案是:當它變得雜亂時,會停止序列化您的現有對象型號。相反,創建一個單獨的DTO模型,該模型設計爲完全以與您選擇的序列化器一起使用,並在序列化之前將數據映射到DTO模型中 - 然後再返回。這可能意味着在DTO模型中使用List<T>而不是IReadOnlyList<T>

0

這可以通過添加一個代理屬性TreeNode返回實現提供給它的構造都IEnumerable<T>Add(T)使用委託的替代包裝類型來完成。首先,介紹以下替代包裝:

// Proxy class for any enumerable with the requisite `Add` methods. 
public class EnumerableProxy<T> : IEnumerable<T> 
{ 
    readonly Action<T> add; 
    readonly Func<IEnumerable<T>> getEnumerable; 

    // XmlSerializer required default constructor (which can be private). 
    EnumerableProxy() 
    { 
     throw new NotImplementedException("The parameterless constructor should never be called directly"); 
    } 

    public EnumerableProxy(Func<IEnumerable<T>> getEnumerable, Action<T> add) 
    { 
     if (getEnumerable == null || add == null) 
      throw new ArgumentNullException(); 
     this.getEnumerable = getEnumerable; 
     this.add = add; 
    } 

    public void Add(T obj) 
    { 
     // Required Add() method as documented here: 
     // https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer%28v=vs.100%29.aspx 
     add(obj); 
    } 

    #region IEnumerable<T> Members 

    public IEnumerator<T> GetEnumerator() 
    { 
     return (getEnumerable() ?? Enumerable.Empty<T>()).GetEnumerator(); 
    } 

    #endregion 

    #region IEnumerable Members 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 

    #endregion 
} 

下一步,修改你的TreeNode通過標記Children[XmlIgnore]並添加一個返回預分配EnumerableProxy<TreeNode>替代屬性:

[XmlRoot(ElementName = "node")] 
public class TreeNode 
{ 
    List<TreeNode> _children = new List<TreeNode>(); 

    [XmlIgnore] 
    public IReadOnlyList<TreeNode> Children { get { return _children; } } 

    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)] 
    [XmlElement(ElementName = "node")] 
    public EnumerableProxy<TreeNode> ChildrenSurrogate 
    { 
     get 
     { 
      return new EnumerableProxy<TreeNode>(() => _children, n => AddChild(n)); 
     } 
    } 

    [XmlAttribute(DataType = "string", AttributeName = "name")] 
    public string Name { get; set; } // some other filed 

    public void AddChild(TreeNode node) 
    { 
     // ... some code 
     _children.Add(node); 
    } 
} 

你的類型,現在是完全序列化並由XmlSerializer反序列化。工作.NET fiddle

該解決方案利用了以下記錄的行爲XmlSerializer。首先,在Remarks for XmlSerializer說:

XmlSerializer的給予特別的待遇,實現IEnumerable或ICollection的類。實現IEnumerable的類必須實現公開的採用單個參數的方法Add。所述Add方法的參數必須是相同的類型從Current屬性返回來自GetEnumerator,或該類型的基地之一返回的值。

因此,你的代理IEnumerable<T>包裝實際上並不需要實現ICollection<T>其全套的方法,包括Clear()Remove()Contains()等。只要有正確的簽名Add()就足夠了。 (如果你想實現,比如說類似的解決方案,Json.NET,您的替代類型需要實現ICollection<T> - 但你可以只從不必要的方法,如Remove()Clear()拋出異常。)

其次,在Introducing XML Serialization說:

XML序列化不會轉換方法,索引器,私有字段或只讀屬性(除只讀的集合)

I.e. XmlSerializer可以成功反序列化預先分配的集合中的項目,即使該集合是由只讀屬性返回的。這樣可以避免需要爲代理屬性或代理集合包裝類型的默認構造函數實現set方法。

相關問題