2015-05-08 3 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> 

То есть агрегация сериализуется как вложенные элементы, а направленная ассоциация сериализуется со ссылкой на идентификатор.


Я хотел бы добиться этого с C# код без необходимости писать пользовательские сериализаторы XML, предпочтительно, просто добавив атрибут, что-то вроде:

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

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

Существует ли что-нибудь подобное? Я взглянул на схему XML, но, похоже, он не обеспечивает то, что мне нужно здесь.


Bonus: Было бы еще лучше, если бы я мог генерировать промежуточные 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 и как третий игрок в Players.

Если вы хотите большего контроля, вам, скорее всего, придется реализовать это все самостоятельно.

+0

Это почти соответствовало бы моим потребностям, за исключением того, что я действительно не хочу, чтобы сериализатор определял, где встроить объект и где ссылаться, но я думаю, что я не могу переопределить поведение по умолчанию? (За исключением настройки модели, чтобы убедиться, что встроенные объекты предлагаются для сериализатора в первую очередь, что, конечно же, является большой проблемой.) –

+0

Он был создан для переноса данных вокруг, очень мало контроля над тем, как он это делает (в отличие от 'XmlSerializer'). В этом случае вы можете указать 'Order' в атрибутах DataMember' для сериализации« Игроки »- в силу этого игроком с« z: Ref »будет« Капитан ». –

+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; } 
} 

или просто хранить идентификатор в CaptainID на команды для сериализации и десериализации и иметь свойство Капитан игрока игнорируется в XML-сериализации.

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); } } 
} 
Смежные вопросы