web-dev-qa-db-fra.com

Utilisez l'attribut XmlInclude ou SoapInclude pour spécifier des types inconnus statiquement.

J'ai un problème très étrange lorsque je travaille avec XmlSerializer .NET.

Prenez les exemples de cours suivants:

public class Order 
{
    public PaymentCollection Payments { get; set; }

    //everything else is serializable (including other collections of non-abstract types)
}

public class PaymentCollection : Collection<Payment>
{
}

public abstract class Payment 
{
    //abstract methods
}

public class BankPayment : Payment
{
    //method implementations
}

Autant que je sache, il existe trois méthodes différentes pour résoudre le InvalidOperationException causé par le sérialiseur ignorant les types dérivés de Payment.

1. Ajout de XmlInclude à la définition de classe Payment:

Ce n'est pas possible car toutes les classes sont incluses en tant que références externes sur lesquelles je n'ai aucun contrôle.

2. Passer les types des types dérivés lors de la création de l'instance XmlSerializer

Ça ne marche pas.

3. Définir XmlAttributeOverrides pour la propriété cible afin de remplacer la sérialisation par défaut de la propriété (comme expliqué dans this SO post )

Aussi ne fonctionne pas (XmlAttributeOverrides initialisation suit).

Type bankPayment = typeof(BankPayment);

XmlAttributes attributes = new XmlAttributes();
attributes.XmlElements.Add(new XmlElementAttribute(bankPayment.Name, bankPayment));

XmlAttributeOverrides overrides = new XmlAttributeOverrides();
overrides.Add(typeof(Order), "Payments", attributes);

Le constructeur approprié XmlSerializer serait alors utilisé.

NOTE: par ne fonctionne pas Je veux dire que le InvalidOperationException (BankPayment n'était pas prévu ...) est levé .

Quelqu'un peut-il nous éclairer sur le sujet? Comment peut-on s'y prendre et déboguer le problème davantage?

81
lsoliveira

Juste résolu le problème. Après avoir creusé un peu plus longtemps, j'ai trouvé this SO post qui couvre exactement la même situation. Cela m'a mis sur la bonne voie.

Fondamentalement, le XmlSerializer doit connaître l'espace de nom par défaut si les classes dérivées sont incluses en tant que types supplémentaires. La raison exacte pour laquelle cela doit arriver est encore inconnue, mais la sérialisation fonctionne toujours.

37
lsoliveira

Cela a fonctionné pour moi:

[XmlInclude(typeof(BankPayment))]
[Serializable]
public abstract class Payment { }    

[Serializable]
public class BankPayment : Payment {} 

[Serializable]
public class Payments : List<Payment>{}

XmlSerializer serializer = new XmlSerializer(typeof(Payments), new Type[]{typeof(Payment)});
80
bizl

Base sur this J'ai pu résoudre ce problème en changeant le constructeur de XmlSerializer que j'utilisais au lieu de changer les classes.

Au lieu d'utiliser quelque chose comme ceci (suggéré dans les autres réponses):

[XmlInclude(typeof(Derived))]
public class Base {}

public class Derived : Base {}

public void Serialize()
{
    TextWriter writer = new StreamWriter(SchedulePath);
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<Derived>));
    xmlSerializer.Serialize(writer, data);
    writer.Close();
}

J'ai fait ça:

public class Base {}

public class Derived : Base {}

public void Serialize()
{
    TextWriter writer = new StreamWriter(SchedulePath);
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<Derived>), new[] { typeof(Derived) });
    xmlSerializer.Serialize(writer, data);
    writer.Close();
}
0
derekantrican