2016-10-07 1 views
0

Utilisation du XmlSerializer .NET via le concepteur de paramètres Visual Studio (code généré automatiquement), il est possible de sérialiser un tableau de types dérivés comme ceci:Sérialisation types dérivés - pas dans un tableau

[XmlArrayItem(Type = typeof(Square)), XmlArrayItem(Type = typeof(Triangle))] 
public Shape[] Shapes; 

... 

Properties.Settings.Default.Save(); 

et cela produit XML comme

<Shape> 
    <Triangle>....</Triangle> 
    <Triangle>....</Triangle> 
    <Square>....</Square> 
</Shape> 

Mais que se passe-t-il si le type dérivé n'est pas dans un tableau (ou toute autre collection)?

Remplacement XmlArrayItem avec XmlElement, les éléments suivants

[XmlElement(Type = typeof(Square)), XmlElement(Type = typeof(Triangle))] 
public Shape Window; 

œuvres, mais produit

<Triangle>....</Triangle> 

où le nom de l'élément est le type, pas le nom de la propriété, et si j'ajoute une deuxième forme:

[XmlElement(Type = typeof(Square)), XmlElement(Type = typeof(Triangle))] 
public Shape Door; 
[XmlElement(Type = typeof(Square)), XmlElement(Type = typeof(Triangle))] 
public Shape Window; 

la sérialisation échoue juste - après tout, ho w serait-il savoir sur la désérialisation quel élément XML va dans quelle propriété?

Y a-t-il un attribut qui me manque? Puis-je faire ce travail sans écrire de code pour personnaliser la sérialisation? Comment?

+0

Le sérialiseur ne produit que la sortie unidirectionnelle sans options. Si vous voulez une sortie de type différente, vous devez générer le fichier XML en utilisant d'autres méthodes de bibliothèque réseau. – jdweng

+0

Le problème qu'ils posent est celui d'une classe héritant d'une autre classe. Le sérialiseur ajoute un type d'attribut au fichier XML. Ils veulent que le type hérité soit un nom d'élément. – jdweng

Répondre

1

Ajout d'attributs [XmlInclude] sur l'objet racine comme indiqué dans this answer est une bonne solution car il gère toutes les occurrences de propriétés polymorphes de type Square n'importe où dans le modèle de données.

Néanmoins, cette situation peut également être gérée à l'aide [XmlElement] attributs par disambiguating les éléments polymorphes via XmlElementAttribute.ElementName:

public class Room 
{ 
    [XmlElement("DoorSquare", Type = typeof(Square)), XmlElement("DoorTriangle", Type = typeof(Triangle))] 
    public Shape Door { get; set; } 

    [XmlElement("WindowSquare", Type = typeof(Square)), XmlElement("WindowTriangle", Type = typeof(Triangle))] 
    public Shape Window { get; set; } 
} 

Ceci produit le code XML suivant:

<Room> 
    <DoorTriangle /> 
    <WindowSquare /> 
</Room> 

Sample fiddle.

-1

Un premier projet à sérialisation forme composite:

[Serializable()] 
    [XmlRoot("shape", Namespace = "", IsNullable = false)] 
    public abstract class Shape { 
    public abstract void Draw(); 
    [XmlAttribute("name")] public string Name { get; set; } 
    } 

    [Serializable()] 
    [XmlRoot("triangle", Namespace = "", IsNullable = false)] 
    public class Triangle : Shape { 
    public override void Draw() { } 
    } 

    [Serializable()] 
    [XmlRoot("square", Namespace = "", IsNullable = false)] 
    public class Square : Shape { 
    public override void Draw() { } 
    } 

    [Serializable()] 
    [XmlRoot("compositeShape", Namespace = "", IsNullable = false)] 
    public class CompositeShape : Shape { 
    [XmlElement("shape", typeof(Shape))] 
    [XmlElement("triangle", typeof(Triangle))] 
    [XmlElement("square", typeof(Square))] 
    [XmlElement("compositeShape", typeof(CompositeShape))] 
    public Shape[] Items { get; set; } 

    public override void Draw() { } 
    } 

utiliser comme:

var shape = new CompositeShape() { 
    Name = "some composite shape", 
    Items = new Shape[] { 
     new CompositeShape() { 
     Name = "inner composite shape", 
     Items = new Shape[] { 
      new Triangle() {Name = "level 2 triangle"}, 
      new Square() {Name="level 2 square"} 
     } }, 
     new Triangle() {Name = "level 1 triangle"}, 
     new Square() {Name="level 1 square"} 
    }}; 
    // serialize ... 

Exemple de sortie:

<compositeShape name="some composite shape"> 
    <compositeShape name="inner composite shape"> 
    <triangle name="level 2 triangle" /> 
    <square name="level 2 square" /> 
    </compositeShape> 
    <triangle name="level 1 triangle" /> 
    <square name="level 1 square" /> 
</compositeShape> 

L'inconvénient de cette approche est que chaque fois que vous ajouter un objet à votre hiérarchie, vous devez également décorer le tableau de forme avec un élément de ce type e, donc pas vraiment fermé pour modification ...

+0

C'est un tableau d'objets Shape. Ma question concerne spécifiquement le cas où ce n'est * pas * dans un tableau. –

+0

Ensuite, c'est une forme qui n'est pas un composite, un triangle ou un carré –

2

La réponse est d'utiliser XmlInclude sur la classe de base au lieu de XmlElement sur les propriétés:

[XmlInclude(typeof(Triangle))] 
[XmlInclude(typeof(Square))] 
public abstract class Shape 

Cela génère XML un peu différent. Pour le cas du tableau:

<Shapes> 
    <Shape xsi:type="Triangle">....</Shape> 
    <Shape xsi:type="Triangle">....</Shape> 
    <Shape xsi:type="Square">....</Shape> 
</Shapes> 

et pour le cas scalaire:

<Door xsi:type="Square">....</Door> 
<Window xsi:type="Triangle">....</Window> 

Parfait.