2015-05-08 33 views
1

通常,數據結構不是嚴格分層的。例如,考慮一個團隊,包括球員和標記的那些玩家作爲隊長之一:在XML序列化中混合嵌套元素和引用元素

enter image description here

如果我想序列化這個模型到XML的一個實例,我希望像一些結構:

<Team id="team1"> 
    <Players> 
     <Player id="player1"/> 
     <Player id="player2"/> 
     <Player id="player3"/> 
     <Player id="player4"/> 
    </Players> 
    <Captain ref="player3"/> 
</Team> 

也就是說,聚集被序列化爲嵌套元素,並且定向關聯被序列化爲對id的引用。


我想實現這個從C#代碼,而無需編寫自定義XML序列化,最好是剛加入的屬性,喜歡的東西:

class Team 
{ 
    public List<Player> Players { get; set; } 

    [XmlReference] 
    public Player Captain { get; set; } 
} 

是否像這樣的東西存在嗎?我看了一下XML模式,但似乎沒有提供我需要的東西。


獎金:它會更好,如果我能產生中間的C#代碼,設置適當的屬性,直接從UML - 或與此有關的任何其他建模形式主義。最後,我只希望能夠在高抽象層次上指定關聯的類型,並使XML序列化符合這個標準,同時以最少的努力/空間來排除錯誤。

+0

您可以引入一個隊長級與您的序列化屬性,而不是元素的樓盤(這樣做將需要XmlAttribute屬性) – HimBromBeere

+1

你介意什麼XML實際上樣子? 'DataContractSerializer'提供了'IsReference'屬性(通常具有更好的性能)。 –

回答

0

您可以創建隊長級如下:

class Captain { 
    [XmlAttribute("ref")] 
    public string reference {get;set;} 
} 

現在你的團隊有這樣的:

class Team 
{ 
    public List<Player> Players { get; set; } 

    public Captain Captain { get; set; } 
} 
+0

是的,這可以用於序列化,但我覺得它從代碼的角度來看頗具侵略性。我不能再使用'someTeam.Captain'來獲得隊長對象,這是不可取的。 –

+0

@VincentvanderWeele這當然是對的,你可以在你的隊長級中包含實際的玩家作爲屬性並且調用team.Captain.Player,但是這不能用於自動方法。 – HimBromBeere

1

正如我在評論中提到,如果你不挑剔的實際XML本身然後DataContractSerializer可以處理這些類型的引用語義。

您添加這樣的屬性:

[DataContract] 
public class Team 
{ 
    [DataMember] 
    public List<Player> Players { get; set; } 
    [DataMember] 
    public Player Captain { get; set; } 
} 

[DataContract(IsReference = true)] 
public class Player 
{ 

} 

和一隊像這樣的:

var p1 = new Player(); 
var p2 = new Player(); 
var p3 = new Player(); 

var team = new Team 
{ 
    Players = new List<Player> 
    { 
     p1, 
     p2, 
     p3 
    }, 
    Captain = p3 
}; 

將被序列化到XML這樣的:

<Team xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication1"> 
    <Captain z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" /> 
    <Players> 
     <Player z:Id="i2" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" /> 
     <Player z:Id="i3" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" /> 
     <Player z:Ref="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" /> 
    </Players> 
</Team> 

在反序列化,同樣的對象會出現爲Captain,並且作爲中的第三位玩家名單。

如果你想要更多的控制,那麼你可能將不得不自己實現這一切。

+0

這幾乎適合我的需求,除了我真的不希望序列化程序決定將內聯對象和引用的位置,但我想我不能覆蓋默認行爲? (除了調整模型以確保首先向串行器提供內聯對象,當然這是一個很大的禁忌) –

+0

它是爲了轉移數據而構建的,對於它如何執行操作幾乎沒有控制權(不像'XmlSerializer')。您可以在DataMember屬性中指定'Order'來首先序列化'Players' - 因此,具有'z:Ref'的玩家將成爲'Captain'。 –

+0

我假設你在其他地方使用這個輸出,而不是隻是再次讀回串行器? –

0

首先,我不知道任何想用XML的東西。雖然我不是專家,只是使用XML進行序列化/反序列化。儘管如此,我可能會爲您提供可行的解決方案。 你可以在MSDN

找到很多的信息如果你有一個模式文件,你可以使用XSD來創建類。 我通常會反過來創建類,然後爲它創建一個模式來驗證我收到的數據。

我要麼用在玩家的屬性分辨出誰是隊長,因此對團隊的屬性只返回其IsCaptain球員==真

class Team 
{ 
    public List<Player> Players { get; set; } 
    public Player Captain { get { return Players.Find(p => p.IsCaptain); } } 
} 
class Player 
{ 
    public string ID { get; set; } 
    public bool IsCaptain { get; set; } 
} 

或只是在團隊存儲的ID在CaptainID爲序列化和反序列化,並在xml序列化中忽略屬性Player Captain。

class Team 
{ 
    public List<Player> Players { get; set; } 
    public string CaptainID { get; set; } 

    [NonSerialized] 
    [System.Xml.Serialization.XmlIgnore] 
    public Player Captain { get { return Players.Find(p => p.ID == CaptainID); } } 
}