Этот ответ предназначен в качестве дополнения к ответу @BorisModylevsky дал. Каждое решение, которое я мог найти для этой проблемы, предлагало использовать IXmlSerializable
. Но вот моя проблема: у меня есть иерархическая родительско-дочерняя структура, подобная этой
public class Project
{
//Several properties here...
public ItemCollection Items { get; private set; }
public Project()
{
Items = new ItemCollection();
}
}
public class ItemCollection : IList<ItemDetails>
{
private List<ItemDetails> _list = new List<ItemDetails>();
public Project Parent { get; private set; }
///dependency injection...
public ItemCollection(Project parent)
{
this.Parent = parent;
}
//Interface methods here working with _list...
//In the `Add(ItemDetails item)` method, I update incoming items with item.Parent = this.Parent.
}
public class ItemDetails
{
public Project Parent { get; set; }
//Several properties here, including some sub classes
///dependency injection...
public ItemDetails(Project parent)
{
this.Parent = parent;
}
}
Чтобы сделать эту работу для DataContractSerializer
Мне пришлось добавить несколько частных конструкторов и необходимые атрибуты:
[DataContract(IsReference=true)]
public class Project
{
[DataMember(Order=0)]
public ItemCollection Items { get; private set; }
//[DataMember(Order=##)]
//Several properties here, including some other sub classes
public Project()
{
Items = new ItemCollection();
}
}
[CollectionDataContract(IsReference=true)]
public class ItemCollection : IList<ItemDetails>
{
private List<ItemDetails> _list = new List<ItemDetails>();
[DataMember(Order=0)]
public Project Parent { get; private set; }
///dependency injection...
public ItemCollection(Project parent)
{
this.Parent = parent;
}
//Private constructor for use with DataContractSerializer
private ItemCollection() { }
//Interface methods here working with _list...
}
[DataContract(IsReference=true)]
public class ItemDetails
{
[DataMember(Order=0)]
public Project Parent { get; private set; }
//[DataMember(Order=##)]
//Several properties here, including some sub classes
///dependency injection...
public ItemDetails(Project parent)
{
this.Parent = parent;
}
//Private constructor for use with DataContractSerializer
private ItemDetails() { }
}
Проблема в том, что XML, созданный для коллекции, выглядел так:
<Project>
<!--OTHER PROJECT PROPERTIES HERE-->
<Items z:Id="i11">
<ItemDetails z:Id="i12">
<Parent z:Ref="i1"/>
<!--OTHER PROPERTIES HERE-->
</ItemDetails>
<ItemDetails z:Id="i16">
<Parent z:Ref="i1"/>
<!--OTHER PROPERTIES HERE-->
</ItemDetails>
</Items>
</Project>
В XML нет свойства Parent
под Items
. Насколько я могу судить, CollectionDataContract
не поддерживает никаких дополнительных свойств в классе коллекции.
В этом случае, если бы я реализовал IXmlSerializable
, как предлагали многие, тогда я застрял бы вручную, сериализовав и десериализовав огромное количество структур внутри ItemDetails (которые я не показал здесь для краткости). В этом случае DataContractSerializer будет почти бесполезен для меня.
Но с ответом @BorisModylevsky выше, я изменил код до этого:
[DataContract(IsReference=true)]
public class Project
{
[DataMember(Order=0)]
public ItemCollection Items { get; private set; }
//[DataMember(Order=##)]
//Several properties here, including some other sub classes
public Project()
{
Items = new ItemCollection();
}
}
[DataContract(IsReference=true)]
public class ItemCollection : IList<ItemDetails>
{
//Refactored _list to ItemsList. I didn't have to; I could have used [DataMember(Name="ItemsList", Order=1)]
[DataMember(Order=1)]
private List<ItemDetails> ItemsList = new List<ItemDetails>();
[DataMember(Order=0)]
public Project Parent { get; private set; }
///dependency injection...
public ItemCollection(Project parent)
{
this.Parent = parent;
}
//Private constructor for use with DataContractSerializer
private ItemCollection() { }
//Interface methods here working with ItemsList...
}
[DataContract(IsReference=true)]
public class ItemDetails
{
[DataMember(Order=0)]
public Project Parent { get; private set; }
//[DataMember(Order=##)]
//Several properties here, including some sub classes
///dependency injection...
public ItemDetails(Project parent)
{
this.Parent = parent;
}
//Private constructor for use with DataContractSerializer
private ItemDetails() { }
}
Подводя итог: я изменил CollectionDataContract
на DataContract
и добавил DataMember
в личный список, который я использую в коллекции. Это работает, так как DataContractSerializer
может получать и устанавливать частные свойства / поля. И теперь XML выглядит так:
<Project>
<!--OTHER PROJECT PROPERTIES HERE-->
<Items z:Id="i11">
<Parent z:Ref="i1"/>
<ItemsList>
<ItemDetails z:Id="i12">
<Parent z:Ref="i1"/>
<!--OTHER PROPERTIES HERE-->
</ItemDetails>
<ItemDetails z:Id="i16">
<Parent z:Ref="i1"/>
<!--OTHER PROPERTIES HERE-->
</ItemDetails>
</ItemsList>
</Items>
</Project>
Что для моих целей работает отлично. При необходимости сериализуется и десериализуется.