posts - 644, comments - 2003, trackbacks - 137

My Links

News

Raffaele Rialdi website

Su questo sito si trovano i miei articoli, esempi, snippet, tools, etc.

Archives

Post Categories

Image Galleries

Blogs

Links

Reflection e le proprietà "specchiate"

Ho finalmente concluso la riscrittura completa di una generica implementazione di IXmlSerializable.

Ovviamente per essere generica usa reflection e non ho fatto l'ottimizzazione dell'implementazione di default, cioè la generazione al volo con CodeDom del codice per serializzare/deserializzare, in quanto per i progetti in cui mi serve la performance di questo codice non è, al momento, critica. In più ci sono già voluti alcuni giorni per considerare tutti i casi imposti dagli attributi XmlElement, XmlChoice, XmlArray, etc. etc.

Perché rifarla da zero? Perché abbiamo bisogno di due cose:

  1. Avere un evento nella entity prima e dopo la serializzazione (cosa non banale quando la entity è in mezzo ad un mare di oggetti dentro un domain model).
  2. Poter modificare liberamente il risultato della serializzazione senza dover riscrivere ad-hoc il codice per ogni singola entity (lavoro massacrante).

Purtroppo riscrivere un XmlWriter non aiuta in modo granulare e così via alla riscrittura. Adesso ogni entity riceve, se vuole, un evento con un XDocument (superbo modo di gestire XML) che può modificare come più gli piace dopo la serializzazione e prima della deserializzazione.

 

Durante le varie manipolazioni con Reflection ho incontrato una piccola stranezza. Quando chiamo GetProperties l'array delle proprietà viene restituito in modo comodo a chi l'ha implementato, ma anche in modo "illogico".
La lista infatti è in ordine di dichiarazione della classe ma nella gerarchia sono elencate le proprietà a partire dalla classe in questione a scendere verso quella base.

Il serializzatore XML di default invece (a meno di non specificare un Order preciso) serializza dalla base risalendo la gerarchia, cosa che appare logica visto che una classe derivata "specializza" e quindi di fatto aggiunge delle proprietà alle classi di base.

Da qui il listato che segue, probabilmente ancora ottimizzabile ma perfetto per il mio scopo. Ovviamente fare il reverse delle proprietà non andrebbe bene perché il loro ordine è corretto per ogni livello della gerarchia di derivazione.

   1: /// <summary>
   2: /// Same as Type.GetProperties, but returning properties in the correct order
   3: /// The problem is that Type.GetProperties returns derived properties first,
   4: /// while the XML Serializer want the base properties first.
   5: /// </summary>
   6: /// <param name="t"></param>
   7: /// <returns></returns>
   8: private PropertyInfo[] GetProperties(Type t)
   9: {
  10:     //PropertyInfo[] properties = t.GetProperties(
  11:     //    BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
  12:  
  13:     List<PropertyInfo> props = new List<PropertyInfo>();
  14:     List<Type> types = new List<Type>();
  15:     // Take the list of all base types
  16:     do
  17:     {
  18:         types.Add(t);
  19:         t = t.BaseType;
  20:     }
  21:     while(t != typeof(object));
  22:     // invert the type list.
  23:     // After this the list start from the base types to the derived ones
  24:     types.Reverse();
  25:  
  26:     foreach(Type x in types)
  27:     {
  28:         // for each type in the list, I take only the public properties
  29:         // It's foundamental to get only the properties declared in that level
  30:         // by using "DeclaredOnly" binding flag.
  31:         PropertyInfo[] properties = x.GetProperties(
  32:             BindingFlags.GetProperty | BindingFlags.Instance | 
  33:             BindingFlags.Public | BindingFlags.DeclaredOnly);
  34:         props.AddRange(properties);
  35:     }
  36:     // finally I can return the PropertyInfo array
  37:     return props.ToArray<PropertyInfo>();
  38: }

Sto anche pensando di pubblicare la serializzazione XML su Codeplex, ma non so se sarebbe di interesse...

Print | posted on domenica 23 marzo 2008 19:22 |

Feedback

Gravatar

# re: Reflection e le proprietà "specchiate"

Se ho capito bene quello che intendi, fai attenzione Raf quando dici "La lista [delle Properties] infatti è in ordine di dichiarazione della classe ", perchè su MSDN di GetProperties dicono
"The GetProperties method does not return properties in a particular order, such as alphabetical or declaration order. Your code must not depend on the order in which properties are returned, because that order varies."

Nella maggior parte dei casi, cioè, GetProperties restituirà le proprietà in base all'ordine in cui sono state dichiarate, ma a volte, in modo casuale, potrebbero essere restituite in un altro ordine, per cui non ci si può basare su questo fattore se effettivamente serve averle ordinate per come sono state dichiarate nella classe.

Questa particolarità mi aveva tratto in inganno nel passaggio dal fx 1.x al 2 (nel mio caso si era trattato di Fields invece che di Properties, ma non cambia la sostanza): nel fx 1, effettivamente i fields venivano sempre restituiti nel modo con cui erano stati dichiarati nella classe, ma nell'msdn già c'era quella 'clausola' (che io non avevo notato, dato che non avevo avuto mai alcun problema). Nel fx 2, invece, hanno cambiato comportamento di GetFields, per cui ogni tanto i fields venivano effettivamente restituiti in modo casuale.
24/03/2008 11:07 | Stefano Ottaviani
Gravatar

# re: Reflection e le proprietà "specchiate"

Ho avuto anche io in passato l'esigenza di recuperare via reflection le properties di una classe nell'ordine di dichiarazione, e alla fine, nonostanze l'affermazione che ha riportato Stefano, ho fatto la stessa scelta di Raffaele.

Vi aggiungo un link a un post di John Wood che cita la spiegazione di questa "scelta" in base a quanto riferito da uno sviluppatore MS: http://www.dotnetjunkies.com/WebLog/johnwood/archive/2005/06/28/128723.aspx
25/03/2008 12:49 | Marco Ragogna
Gravatar

# re: Reflection e le proprietà "specchiate"

Nel tuo caso, Raf, concordo...e mi sembra che quello che ho evidenziato non costituisca un particolare problema.

Volevo più che altro evidenziare questa particolarità di GetProperties / GetFields, che in altre situazioni, invece, possono dare problemi se ci si affida all'ordine di dichiariazione...facendo il primo esempio 'semplice' che mi viene in mente, se invece di un file xml si voleva usare un file csv, in cui le posizioni dei campi contano, li si sarebbero potuti avere dei bug a runtime.
In quel caso, per avere un comportamento sempre corretto, bisognerebbe associare un attributo per l'effettivo ordine dei campi / proprietà che si vuole ottenere, e non basarsi nella posizione con cui sono stati definiti all'interno della classe.
25/03/2008 14:02 | Stefano Ottaviani
Gravatar

# re: Reflection e le proprietà "specchiate"

Il post è molto interessante e poco rassicurante :)
Fortunatamente nella mia versione della serializzazione supporto la clausola Order nei vari attributi, perciò se ci fossero problemi di Xml che non rispetti lo schema, questi verrebbero subito appianati applicando questa clausola.
Se invece non c'è un constraint sullo schema XSD, allora la deserializzazione che ho scritto non si cura dell'ordine "casuale" delle proprietà e quindi funzionerebbe anche nel caso in cui la GetProperties dovesse dare risultati differenti tra serializzazione e deserializzazione.
25/03/2008 14:04 | Raffaele Rialdi
Gravatar

# re: Reflection e le proprietà "specchiate"

@Stefano. Hai ragione, ma io devo emulare quello che fa il serializzatore di default e non ho alternative.
Ad ogni modo come scrivevo nell'altro commento il problema viene solo nel caso in cui la serializzazione debba essere validata a fronte di uno schema XSD. Per la deserializzazione invece l'ordine delle proprietà non mi causa alcun problema.
25/03/2008 14:07 | Raffaele Rialdi
Gravatar

# re: Reflection e le proprietà "specchiate"

Due anni fa, notai anche io un problema nell'ordine degli attributi multi-use: http://blogs.ugidotnet.org/adrian/archive/2006/02/13/34771.aspx - so che non c'entra con GetProperties ma riguarda sempre i metadati
25/03/2008 15:49 | Adrian Florea
Gravatar

# re: Reflection e le proprietà "specchiate"

Raffaele, penso che tu non sia l'unico ad avere il problema di implementare IXmlSerializable generico, quindi credo che metterlo su CodePlex potrebbe essere interessante. Anche io, ne avevo iniziato un'implementazione qualche tempo fa, poi abbiamo deciso di scendere a compromessi e usare il serializzatore xml out of the box.
27/03/2008 13:35 | stefano martinz
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET