Esta respuesta está pensado como un complemento a la respuesta @BorisModylevsky dio. Cada solución que pude encontrar para este problema sugirió usar IXmlSerializable
. Pero aquí es mi problema: Tengo una estructura padre-hijo jerárquica como esto
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;
}
}
Con el fin de hacer este trabajo para DataContractSerializer
he tenido que añadir algunos constructores privados, y los atributos necesarios:
[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() { }
}
el problema es que se crea el XML para la recogida era la siguiente:
<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>
no hay Parent
propiedad bajo Items
en el XML. Lo mejor que puedo decir es que CollectionDataContract
no admite ninguna propiedad adicional en la clase de recopilación.
En este caso, si implementé IXmlSerializable
como muchos habían sugerido, entonces iba a estar atascado manualmente serializando y deserializando una gran cantidad de las estructuras dentro de ItemDetails (que no he mostrado aquí por brevedad). En cuyo caso, DataContractSerializer sería inútil para mí.
Pero con @BorisModylevsky respuesta anterior, he cambiado el código para esto:
[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() { }
}
En resumen: me cambiaron a CollectionDataContract
DataContract
y ha añadido a la lista DataMember
privada que estoy utilizando en la colección. Esto funciona desde DataContractSerializer
puede obtener y establecer propiedades privadas/campos. Y ahora el XML se ve así:
<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>
Cuál para mis propósitos funciona perfectamente. Se serializa y deserializa según sea necesario.
Buena publicación en el blog, pero sería bueno que dieras un breve resumen aquí. –
¿Esto no expone un elemento '' en el XML? La solución preferible sería tener 'AdditionalProperty' como un atributo XML de' EntityCollectionWorkaround' y luego tener cada elemento '' como hijo directo de 'EntityCollectionWorkaround'. Pero supongo que esto es imposible con la serialización de DataContract? –
Sí, lo hace, pero no veo qué pasa con esto? Supongo que su sugerencia implica que se utiliza algún tipo de serialización XML. Prefiero separar el método de serialización (binario, xml o lo que sea) y la definición de clase. Aunque adoptemos su sugerencia, AdditionalProperty estará expuesto en XML. –