J'ai récemment enregistré des gestionnaires d'événements pour des nœuds inconnus, des éléments et des attributs avec le XMLSerializer que j'utilise pour désérialiser des types complexes d'une hiérarchie de types. Je l'ai fait parce qu'une partie du XML que je reçois provient de tiers; Je suis intéressé par les changements de format de données qui pourraient causer des problèmes de ma part.XMLSerializer met en garde contre des nœuds/attributs inconnus lors de la désérialisation de types dérivés
Dans XML, le XMLSerializer génère l'attribut XML standard xsi:type="somederivedtypename"
pour identifier le type dérivé réel représenté par un élément XML.
J'ai été surpris de voir que le même sérialiseur traite ce même attribut qu'il vient de produire comme inconnu lors de la désérialisation. Fait intéressant cependant, la désérialisation est correcte et complète (également avec des types plus complexes et des données dans mon programme du monde réel). Cela signifie que le sérialiseur évalue correctement les informations de type au début de la désérialisation. Mais lors d'une étape ultérieure d'extraction de données, l'attribut est apparemment confondu avec une partie de données vraie de l'objet, ce qui est bien sûr inconnu.
Dans mon application, les avertissements gratuits finissent par encombrer un fichier journal à usage général qui n'est pas souhaité. À mon avis, le sérialiseur devrait relire le XML produit sans hoquet. Mes questions:
- Est-ce que je fais quelque chose de mal?
- Y a-t-il une solution de contournement?
Un exemple minimal est ici:
using System;
using System.IO;
using System.Xml.Serialization;
namespace XsiTypeAnomaly
{
/// <summary>
/// A trivial base type.
/// </summary>
[XmlInclude(typeof(DerivedT))]
public class BaseT{}
/// <summary>
/// A trivial derived type to demonstrate a serialization issue.
/// </summary>
public class DerivedT : BaseT
{
public int anInt { get; set; }
}
class Program
{
private static void serializer_UnknownAttribute
( object sender,
XmlAttributeEventArgs e)
{
Console.Error.WriteLine("Warning: Deserializing "
+ e.ObjectBeingDeserialized
+ ": Unknown attribute "
+ e.Attr.Name);
}
private static void serializer_UnknownNode(object sender, XmlNodeEventArgs e)
{
Console.Error.WriteLine("Warning: Deserializing "
+ e.ObjectBeingDeserialized
+ ": Unknown node "
+ e.Name);
}
private static void serializer_UnknownElement(object sender, XmlElementEventArgs e)
{
Console.Error.WriteLine("Warning: Deserializing "
+ e.ObjectBeingDeserialized
+ ": Unknown element "
+ e.Element.Name);
}
/// <summary>
/// Serialize, display the xml, and deserialize a trivial object.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
BaseT aTypeObj = new DerivedT() { anInt = 1 };
using (MemoryStream stream = new MemoryStream())
{
var serializer = new XmlSerializer(typeof(BaseT));
// register event handlers for unknown XML bits
serializer.UnknownAttribute += serializer_UnknownAttribute;
serializer.UnknownElement += serializer_UnknownElement;
serializer.UnknownNode += serializer_UnknownNode;
serializer.Serialize(stream, aTypeObj);
stream.Flush();
// output the xml
stream.Position = 0;
Console.Write((new StreamReader(stream)).ReadToEnd() + Environment.NewLine);
stream.Position = 0;
var serResult = serializer.Deserialize(stream) as DerivedT;
Console.WriteLine(
(serResult.anInt == 1 ? "Successfully " : "Unsuccessfully ")
+ "read back object");
}
}
}
}
Sortie:
<?xml version="1.0"?>
<BaseT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="DerivedT">
<anInt>1</anInt>
</BaseT>
Warning: Deserializing XsiTypeAnomaly.DerivedT: Unknown node xsi:type
Warning: Deserializing XsiTypeAnomaly.DerivedT: Unknown attribute xsi:type
Successfully read back object
L'avertissement semble avoir du sens car vous créez un sérialiseur pour 'BaseT', puis vous alimente en réalité un objet' DerivedT'. Si vous venez de créer un sérialiseur pour 'DerivedT', l'avertissement disparaît. – jsanalytics
@jstreet Mais le but de cet attribut est de permettre au sérialiseur de la classe de base de désérialiser les objets dérivés. Imaginez une liste de base, qui peut contenir n'importe quel type dérivé. Le code appelant ne sait pas et ne se soucie pas quels types dérivés réels sont détenus dans la liste. Le code de gestion de liste a été réellement écrit avant que beaucoup de types dérivés aient existé. –
Je vois votre point, et le sérialiseur est capable de désérialiser l'objet dérivé, avec "l'incovenience" de l'avertissement, parce qu'il ne connaît pas la propriété 'anInt'. Suggestion: lorsque vous créez votre sérialiseur, utilisez 'aTypeObj.GetType()' au lieu d'utiliser un type explicite, base ou dérivé. – jsanalytics