Gotchas di serializzazione XML .NET?

Ho eseguito alcuni trucchi durante la serializzazione C # XML che pensavo di condividere:

  • Non puoi serializzare articoli di sola lettura (come KeyValuePairs)
  • Non è ansible serializzare un dizionario generico. Prova invece questa class wrapper (da http://weblogs.asp.net/pwelter34/archive/2006/05/03/444961.aspx ):

using System; using System.Collections.Generic; using System.Text; using System.Xml.Serialization; [XmlRoot("dictionary")] public class SerializableDictionary : Dictionary, IXmlSerializable { public System.Xml.Schema.XmlSchema GetSchema() { return null; } public void ReadXml(System.Xml.XmlReader reader) { XmlSerializer keySerializer = new XmlSerializer(typeof(TKey)); XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue)); bool wasEmpty = reader.IsEmptyElement; reader.Read(); if (wasEmpty) return; while (reader.NodeType != System.Xml.XmlNodeType.EndElement) { reader.ReadStartElement("item"); reader.ReadStartElement("key"); TKey key = (TKey)keySerializer.Deserialize(reader); reader.ReadEndElement(); reader.ReadStartElement("value"); TValue value = (TValue)valueSerializer.Deserialize(reader); reader.ReadEndElement(); this.Add(key, value); reader.ReadEndElement(); reader.MoveToContent(); } reader.ReadEndElement(); } public void WriteXml(System.Xml.XmlWriter writer) { XmlSerializer keySerializer = new XmlSerializer(typeof(TKey)); XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue)); foreach (TKey key in this.Keys) { writer.WriteStartElement("item"); writer.WriteStartElement("key"); keySerializer.Serialize(writer, key); writer.WriteEndElement(); writer.WriteStartElement("value"); TValue value = this[key]; valueSerializer.Serialize(writer, value); writer.WriteEndElement(); writer.WriteEndElement(); } } } 

Qualche altra traccia di serializzazione XML là fuori?

Un altro grande trucchetto: quando si esegue l’output di XML attraverso una pagina Web (ASP.NET), non si desidera includere il marchio Ordine byte Unicode . Naturalmente, i modi per utilizzare o non utilizzare il BOM sono quasi gli stessi:

BAD (include BOM):

 XmlTextWriter wr = new XmlTextWriter(stream, new System.Text.Encoding.UTF8); 

BENE:

 XmlTextWriter wr = new XmlTextWriter(stream, new System.Text.UTF8Encoding(false)) 

È ansible passare esplicitamente false per indicare che non si desidera il BOM. Si noti la chiara, ovvia differenza tra Encoding.UTF8 e UTF8Encoding .

I tre byte BOM extra all’inizio sono (0xEFBBBF) o (239 187 191).

Riferimento: http://chrislaco.com/blog/troubleshooting-common-problems-with-the-xmlserializer/

Non posso ancora fare commenti, quindi commenterò il post di Dr8k e faremo un’altra osservazione. Variabili private che vengono esposte come proprietà pubbliche getter / setter e vengono serializzate / deserializzate come tali attraverso tali proprietà. L’abbiamo fatto al mio vecchio lavoro per tutto il tempo.

Una cosa da notare però è che se si ha una logica in queste proprietà, la logica viene eseguita, quindi a volte l’ordine di serializzazione conta davvero. I membri sono implicitamente ordinati in base a come sono ordinati nel codice, ma non ci sono garanzie, specialmente quando si eredita un altro object. Ordinarli esplicitamente è un dolore alle spalle.

Sono stato bruciato da questo in passato.

Quando si serializza in una stringa XML da un stream di memoria, assicurarsi di utilizzare MemoryStream # ToArray () invece di MemoryStream # GetBuffer () o si finirà con caratteri indesiderati che non deserializzeranno correttamente (a causa del buffer aggiuntivo allocato).

http://msdn.microsoft.com/en-us/library/system.io.memorystream.getbuffer(VS.80).aspx

Se il serializzatore incontra un membro / proprietà che ha un’interfaccia come tipo, non verrà serializzato. Ad esempio, quanto segue non serializzerà in XML:

 public class ValuePair { public ICompareable Value1 { get; set; } public ICompareable Value2 { get; set; } } 

Sebbene questo verrà serializzato:

 public class ValuePair { public object Value1 { get; set; } public object Value2 { get; set; } } 

IEnumerables che vengono generati tramite i ritorni di rendimento non sono serializzabili. Questo perché il compilatore genera una class separata per implementare il rendimento restituito e quella class non è contrassegnata come serializzabile.

Non è ansible serializzare le proprietà di sola lettura. Devi avere un getter e un setter, anche se non hai mai intenzione di usare la deserializzazione per trasformare XML in un object.

Per lo stesso motivo, non è ansible serializzare le proprietà che restituiscono interfacce: il deserializzatore non saprebbe quale class concreta istanziare.

Oh, eccone una buona: poiché il codice di serializzazione XML viene generato e inserito in una DLL separata, non si ottiene alcun errore significativo quando si verifica un errore nel codice che interrompe il serializzatore. Solo qualcosa come “Imansible trovare s3d3fsdf.dll”. Bello.

Un’altra cosa da notare: non è ansible serializzare i membri della class privata / protetta se si utilizza la serializzazione XML “predefinita”.

Ma puoi specificare la logica di serializzazione XML personalizzata che implementa IXmlSerializable nella tua class e serializza qualsiasi campo privato che desideri / vuoi.

http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx

Non è ansible serializzare un object che non ha un costrutore parametrico (è stato appena morso da quello).

E per qualche ragione, dalle seguenti proprietà, Value viene serializzato, ma non FullName:

  public string FullName { get; set; } public double Value { get; set; } 

Non sono mai riuscito a capire perché, ho appena cambiato Valore in interno …

Se l’assembly generato dalla serializzazione XML non si trova nello stesso contesto di caricamento del codice che tenta di usarlo, si verificheranno errori impressionanti come:

 System.InvalidOperationException: There was an error generating the XML document. ---System.InvalidCastException: Unable to cast object of type 'MyNamespace.Settings' to type 'MyNamespace.Settings'. at Microsoft.Xml.Serialization.GeneratedAssembly. XmlSerializationWriterSettings.Write3_Settings(Object o) 

La causa di ciò per me era un plugin caricato usando il contesto LoadFrom che ha molti svantaggi nell’utilizzare il contesto Load. Un po ‘divertente rintracciare quella in basso.

È ansible che si verifichino problemi durante la serializzazione di oggetti di tipo Colore e / o Font.

Ecco i consigli che mi hanno aiutato:

http://www.codeproject.com/KB/XML/xmlsettings.aspx

http://www.codeproject.com/KB/cs/GenericXmlSerializition.aspx

Per informazioni dettagliate su cosa è supportato dal serializzatore XML e per i dettagli sul modo in cui sono supportate le funzionalità XSD supportate, vedere ” Attributi linguistici di definizione dello schema XML avanzato .

Se si tenta di serializzare un array, List o IEnumerable che contiene istanze di sottoclassi di T , è necessario utilizzare XmlArrayItemAttribute per elencare tutti i sottotipi utilizzati. Altrimenti si otterrà un System.InvalidOperationException inutile in fase di runtime durante la serializzazione.

Qui fa parte di un esempio completo dalla documentazione

 public class Group { /* The XmlArrayItemAttribute allows the XmlSerializer to insert both the base type (Employee) and derived type (Manager) into serialized arrays. */ [XmlArrayItem(typeof(Manager)), XmlArrayItem(typeof(Employee))] public Employee[] Employees; 

Le variabili / proprietà private non sono serializzate nel meccanismo predefinito per la serializzazione XML, ma sono in serializzazione binaria.

Le proprietà contrassegnate con l’attributo Obsolete non sono serializzate. Non ho provato con l’attributo Deprecated ma presumo che agisca allo stesso modo.

Non posso davvero spiegare questo, ma ho trovato che non si serializzerà:

 [XmlElement("item")] public myClass[] item { get { return this.privateList.ToArray(); } } 

ma questo:

 [XmlElement("item")] public List item { get { return this.privateList; } } 

E vale la pena notare che se stai serializzando su un memstream, potresti voler cercare 0 prima di usarlo.

Fare attenzione ai tipi di serializzazione senza serializzazione esplicita, può causare ritardi mentre .Net li costruisce. L’ho scoperto di recente durante la serializzazione di RSAParameters .

Se il tuo XSD fa uso di gruppi di sostituzione, è probabile che non puoi (de) serializzarlo automaticamente. Avrai bisogno di scrivere i tuoi serializzatori per gestire questo scenario.

Per esempio.

                          

In questo esempio, una busta può contenere messaggi. Tuttavia, il serializzatore predefinito di .NET non distingue tra Message, ExampleMessageA e ExampleMessageB. Serializzerà solo verso e dalla class Message base.

Le variabili / proprietà private non sono serializzate nella serializzazione XML, ma sono in serializzazione binaria.

Credo che questo ti diventi anche se esponi i membri privati ​​tramite proprietà pubbliche: i membri privati ​​non vengono serializzati, quindi i membri pubblici fanno tutti riferimento a valori nulli.