Customized Serializations
In tutte e tre le situazioni precedenti abbiamo considerato di voler sempre serializzare i nostri oggetti accettando il comportamento predefinito dei serializzatori. In realtà si può personalizzare ad un livello decisamente avanzato il loro comportamento, aggiungendo ai membri delle nostre classi degli attributi che dicano loro come serializzare i contenuti. Per esempio se in una classe da serializzare con un BinaryFormatter o un SoapFormatter volessimo eliminare qualche propietà dalla serializzazione, possiamo farlo tramite l’attributo NonSerialized nel modo seguente:
[Serializable]
class clsPersona
{
public String nome;
public Int32 eta;
[NonSerialized]
private String id = Guid.NewGuid().ToString();
}
In questo caso la serializzazione dell’oggetto non conterrà il campo id il quale, alla successiva deserializzazione, sarà reinizializzato al valore predefinito dal tipo che rappresenta (0 per i numeri, stringa vuota per le stringhe, ecc.). È anche possibile prevedere per le classi dei comportamenti personalizzati in fase di serializzazione. Per esempio se volessimo far sì che all’atto della serializzazione i nostri dati vengano criptati o firmati digitalmente potremmo appoggiarci all’interfaccia ISerializable nella definizione delle nostre classi.
public interface ISerializable
{
void GetObjectData(SerializationInfo info,
StreamingContext context);
}
Quando un oggetto viene serializzato infatti, se questo nella sua definizione implementa ISerializable, viene richiamato il suo metodo GetObjectData() e ad esso viene richiesto di fornire i dati da serializzare. L’argomento di tipo SerializationInfo permette di fornire i campi da salvare per mantenere lo stato dell’oggetto. L’argomento di tipo StreamingContext invece ci permette di capire per quale motivo il nostro oggetto debba essere serializzato. Quando serializziamo un oggetto in modalità personalizzata occorre anche deserializzarlo con un criterio analogo. Gli oggetti che presentano una serializzazione personalizzata generalmente prevedono un costruttore apposito:
protected NomeClasse(SerializationInfo info, StreamingContext context)
che viene richiamato dal Formatter nella fase di reidratazione dell’oggetto e che ci permette di rileggere esattamente i valori che avevamo salvato in fase di serializzazione personalizzata. Vediamo un esempio completo:
[Serializable]
class clsPersona: ISerializable
{
public String nome;
public Int32 eta;
private String id = Guid.NewGuid().ToString();
private DateTime dataPersistenza;
public void GetObjectData(SerializationInfo info,
StreamingContext context)
{
info.AddValue("NomeCompleto", nome);
info.AddValue("Eta", eta);
info.AddValue("IDPersona", id);
if(context.State == StreamingContextStates.All)
info.AddValue("DataPersistenza", DateTime.Now);
}
public clsPersona(){}
protected clsPersona(SerializationInfo info,
StreamingContext context)
{
nome = info.GetString("NomeCompleto");
eta = info.GetInt32("Eta");
id = info.GetString("IDPersona");
if(context.State == StreamingContextStates.All)
dataPersistenza = info.GetDateTime("DataPersistenza");
}
}
In questo caso durante la serializzazione dell’oggetto viene salvato un valore personalizzato pari alla data e ora di serializzazione, con nome DataPersistenza. Se infatti valutiamo il risultato di una serializzazione SOAP su un oggetto di questo tipo avremo:
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<a1:clsPersona id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/CustomSerializationTest01
/CustomSerializationTest01%2C%20Version%3D1.0.768.42350%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<NomeCompleto id="ref-3">Mario Rossi</NomeCompleto>
<Eta>32</Eta>
<IDPersona id="ref-4">c3f66cc6-313c-445b-9b06-788a79e1d233</IDPersona>
<DataPersistenza xsi:type="xsd:dateTime">2002-02-07T23:31:44.5823296+01:00</DataPersistenza>
</a1:clsPersona>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
<SOAP-ENV:Body>
<a1:clsPersona id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/CustomSerializationTest01
/CustomSerializationTest01%2C%20Version%3D1.0.768.42350%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<NomeCompleto id="ref-3">Mario Rossi</NomeCompleto>
<Eta>32</Eta>
<IDPersona id="ref-4">c3f66cc6-313c-445b-9b06-788a79e1d233</IDPersona>
<DataPersistenza xsi:type="xsd:dateTime">2002-02-07T23:31:44.5823296+01:00</DataPersistenza>
</a1:clsPersona>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Come si può vedere all’interno del messaggio SOAP non abbiamo i nomi delle proprietà dell’oggetto, ma bensì i valori da noi scelti nel metodo GetObjectData dell’interfaccia ISerializable. La personalizzazione operata in questo esempio risulta in realtà superflua, nel caso in cui l’obiettivo sia solo rinominare i campi nel documento SOAP, in quanto esistono già degli attributi pensati per questo scopo. Acquista maggiore interesse invece la possibilità di codificare o firmare digitalmente i valori prima della serializzazione, per poi controllarne la bontà durante la reidratazione.
[Immagine 04: L’oggetto basato su clsPersona prima e dopo la serializzazione personalizzata]
Attributi per la Serializzazione
Vediamo alcuni degli attributi più interessanti che abbiamo a disposizione per personalizzare la serializzazione XML, senza dover ricorrere all’interfaccia ISerializable, ma semplicemente sfruttando appieno l’oggetto XmlSerializer.
| Attributi relativi alla serializzazione SOAP | |
|---|---|
| SoapAttributeAttribute | Indica che la proprietà pubblica o privata, il parametro o il valore di ritorno a cui viene applicato dovrà essere un attributo nel documento XML |
| SoapElemenAttributet | Indica che la proprietà pubblica o privata, il parametro o il valore di ritorno a cui viene applicato dovrà essere un elemento nel documento XML |
| SoapEnumAttribute | Indica il contenuto testuale del nodo che conterrà una voce di una enumerazione |
| SoapIgnoreAttribute | La proprietà sarà omessa nella serializzazione |
| SoapIncludeAttribute | Permette di includere nel documento SOAP la definizione di tipi derivati |
| SoapTypeAttribute | Si applica solo alle classi e permette di indicare che la classe potrà essere serializzata come un tipo XML secondo XSD |
Proviamone alcuni tramite una classe così definita:
public enum EnumTipoOrdine
{
[SoapEnum("Business To Consumer")]
B2C,
[SoapEnum("Business To Business")]
B2B
}
[Serializable]
[SoapType("Ordine", "http://www.devleap.com/nsordine")]
public class clsOrdine
{
[SoapAttribute("dtOrdine")]
public DateTime dataOrdine;
[SoapAttribute("idOrdine")]
public String id = Guid.NewGuid().ToString();
[SoapElement("NomeCliente")]
public String NomeCliente;
[SoapIgnore]
public Int32 nContatoreInterno = 0;
[SoapElement("TipologiaOrdine")]
public EnumTipoOrdine tipologia;
}
La serializzazione dovrà in questo caso appoggiarsi ad una istanza di XmlSerializer, costruita tramite un XmlTypeMapping. Come risultato avremo un documento XML come il seguente:
<?xml version="1.0"?>
<q1:Ordine xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="id1" q1:dtOrdine="2002-02-08T00:18:23.9576336+01:00" q1:idOrdine="1aa2ac07-7940-4ff0-a398-f994c005cce7" xmlns:q1="http://www.devleap.com/nsordine">
<NomeCliente xsi:type="xsd:string">Mario Rossi</NomeCliente>
<TipologiaOrdine xsi:type="EnumTipoOrdine">Business To Business</TipologiaOrdine>
</q1:Ordine>
<q1:Ordine xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="id1" q1:dtOrdine="2002-02-08T00:18:23.9576336+01:00" q1:idOrdine="1aa2ac07-7940-4ff0-a398-f994c005cce7" xmlns:q1="http://www.devleap.com/nsordine">
<NomeCliente xsi:type="xsd:string">Mario Rossi</NomeCliente>
<TipologiaOrdine xsi:type="EnumTipoOrdine">Business To Business</TipologiaOrdine>
</q1:Ordine>
Dove è possibile vedere il risultato degli attributi assegnati nella definizione della classe:
- Il tag root si chiama Ordine per la presenza dell’attributo SoapTypeAttribute .
- La data e l’id dell’ordine sono attributi del nodo root e non elementi, grazie all’attributo SoapAttributeAttribute .
- Nel tag TipologiaOrdine abbiamo un valore descrittivo (“Business To Business”) e non la sola sigla (“B2B”) utilizzata nel codice, grazie all’attributo SoapEnumAttribute .
Possiamo controllare il risultato della serializzazione XML anche tramite degli attributi specifici che prescindono da SOAP.
| Attributi relativi alla serializzazione XML | |
|---|---|
| XmlAnyAttributeAttribute | Si applicano a proprietà pubbliche o valori di ritorno che restituiscono array di attributi o elementi, rispettivamente. Indicano se nella fase di deserializzazione eventuali attributi o elementi, rispettivamente, privi di corrispondenza nello schema debbano essere rappresentati come array di generici oggetti XmlAttribute o XmlElement. |
| XmlAnyElementAttribute | |
| XmlArrayAttribute | Permette di rappresentare un array di oggetti come array di contenuti XML |
| XmlArrayItemAttribute | Si applica ai tipi che saranno poi utilizzati in un array segnato con l’attributo precedente |
| XmlAttributeAttribute | Indica che dovrà essere presentato tramite un attributo |
| XmlChoiceIdentifierAttribute | Permette di definire nello schema XSD del documento una sezione choice secondo la grammatica XSD |
| XmlElementAttribute | Indica che dovrà essere presentato tramite un elemento |
| XmlEnumAttribute | Definisce il valore corrispondente ad un elemento di una enumerazione |
| XmlIgnoreAttribute | Omette il valore in fase di serializzazione |
| XmlIncludeAttribute | Include nella serializzazione la definizione di altri tipi |
| XmlRootAttribute | Indica il nome del nodo root ed il suo eventuale Namespace |
| XmlTextAttribute | Indica che dovrà essere presentato tramite un nodo di tipo testo |
| XmlTypeAttribute | Indica il nome ed il Namespace del tipo di dato rappresentato |
Proviamo quindi qualcuno di questi attributi con una classe di esempio:
[XmlType(IncludeInSchema = false)]
public enum EnumTipoOrdine
{
[XmlEnum(Name = "Business To Consumer")]
B2C,
[XmlEnum(Name = "Business To Business")]
B2B
}
[XmlRoot(ElementName = "Ordine",
Namespace = "http://www.devleap.com/nsordine")]
public class clsOrdine
{
[XmlAttribute(AttributeName = "dtOrdine")]
public DateTime dataOrdine;
[XmlAttribute(AttributeName = "idOrdine")]
public String id = Guid.NewGuid().ToString();
[XmlElement(ElementName = "NomeCliente",
Namespace = "http://www.devleap.com/nscliente")]
public String NomeCliente;
[XmlIgnore]
public Int32 nContatoreInterno = 0;
[XmlText()]
public String descrizioneMerce;
[XmlElement(ElementName = "TipologiaOrdine")]
public EnumTipoOrdine tipologia;
}
La serializzazione di una classe coma la precedente fornirà un risultato simile a:
<?xml version="1.0"?>
<Ordine xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" dtOrdine="2002-02-08T01:26:19.2375984+01:00" idOrdine="ac895340-6641-496b-9c47-f63199267e40" xmlns="http://www.devleap.com/nsordine">
<NomeCliente xmlns="http://www.devleap.com/nscliente">Mario Rossi</NomeCliente>
Acquisto da serializzare <TipologiaOrdine>Business To Consumer</TipologiaOrdine>
</Ordine>
<Ordine xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" dtOrdine="2002-02-08T01:26:19.2375984+01:00" idOrdine="ac895340-6641-496b-9c47-f63199267e40" xmlns="http://www.devleap.com/nsordine">
<NomeCliente xmlns="http://www.devleap.com/nscliente">Mario Rossi</NomeCliente>
Acquisto da serializzare <TipologiaOrdine>Business To Consumer</TipologiaOrdine>
</Ordine>
Dove ancora una volta possiamo vedere che:
- Il nodo root si chiama Ordine ed ha associato il namespace http://www.devleap.com/nsordine, come richiesto nell’attributo XmlRoot.
- Le proprietà dtOrdine e idOrdine sono rappresentate come attributi a causa della presenza degli attributi XmlAttribute.
- Il testo della proprietà descrizioneMerce figura come nodo di tipo testo all’interno del tag ordine in quanto è segnato con l’attributo XmlText.
- La proprietà tipologia dell’ordine è inclusa in un nodo elemento come richiesto dall’attributo XmlElement. Il suo valore è rappresentato dalla descrizione indicata a livello di attributo XmlEnum nella definizione della enumerazione.
Per ottenere questi risultati, in questo secondo caso, dovremo serializzare l’oggetto sempre tramite un XmlSerializer ma non appoggiandoci al costruttore basato su XmlTypeMapping. È anche possibile fare l’overriding del comportamente di XmlSerializer rispetto a quello che descrivono gli attributi definiti nelle classi. È sufficiente definire un oggetto XmlAttributes, che è una collezione di attributi per XmlSerializer, compilarla con gli attributi che si vogliono definire. Aggiungendo quindi l’istanza di classe XmlAttributes con gli attributi al suo interno ad un oggetto di tipo XmlAttributeOverrides possiamo richiamare il costruttore dell’oggetto XmlSerializer fornendo anche gli override del caso. Vediamo un rapido esempio in cui sostituiamo alla generazione di un attributo di nome “Nome” quella di un nodo di tipo testo all’interno del nodo root del documento XML:
using System;
using System.Xml;
using System.Xml.Serialization;
namespace XmlSerializeOverrideTest01
{
[XmlRoot(ElementName="Persona",
Namespace="http://www.devleap.com/nspersona")]
public class clsPersona
{
[XmlAttribute(AttributeName="IDPersona")]
public String id = Guid.NewGuid().ToString();
[XmlAttribute(AttributeName="Nome")]
public String nome;
}
class clsMain
{
static void Main(string[] args)
{
XmlTextAttribute objTextAttribute = new
XmlTextAttribute();
objTextAttribute.DataType = "string";
XmlAttributes objAttributes = new XmlAttributes();
objAttributes.XmlText = objTextAttribute;
XmlAttributeOverrides objAttrOverrides = new
XmlAttributeOverrides();
objAttrOverrides.Add(typeof(clsPersona), "nome",
objAttributes);
XmlSerializer objXmlSerializer = new
XmlSerializer(typeof(clsPersona), objAttrOverrides);
clsPersona objPersona = new clsPersona();
objPersona.nome = "Mario Rossi";
Console.WriteLine("Risultato della
serializzazione:\n\n\n");
objXmlSerializer.Serialize(Console.Out, objPersona);
Console.WriteLine("\n\n\nPremere INVIO per continuare...");
Console.ReadLine();
}
}
}
Xml Schema Definition Tool (XSD.EXE)
Nel Framework .NET abbiamo anche un tool a riga di comando che si occupa di gestire la serializzazione XML: XSD.EXE. Con questa utility possiamo:
Se costruiamo una Class Library che rapprensenti la classe ordine vista nella sezione precedente, possiamo chiedere ad XSD di generarci gli schema XSD che descrivono le strutture dati in essa presenti. Otterremo lo schema XSD della classe clsOrdine, dell’enumerazione EnumTipoOrdine e della rappresentazione del cliente, in quanto dotato di un suo Namespace separato rispetto a quello che descrive un ordine.
- Convertire un documento XSD in una classe
- Convertire la definizione di una classe all’interno di un assembly in uno schema XSD
- Convertire uno schema XDR in uno schema XSD
- Leggere un’istanza di documento XML e ricavare lo schema XSD corrispondente
Se costruiamo una Class Library che rapprensenti la classe ordine vista nella sezione precedente, possiamo chiedere ad XSD di generarci gli schema XSD che descrivono le strutture dati in essa presenti. Otterremo lo schema XSD della classe clsOrdine, dell’enumerazione EnumTipoOrdine e della rappresentazione del cliente, in quanto dotato di un suo Namespace separato rispetto a quello che descrive un ordine.
[Immagine 05: XSD.EXE all’opera per generare uno schema XSD da una classe in un assembly.]
Lo schema XSD dell’ordine per esempio sarà:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:tns="http://www.devleap.com/nsordine" elementFormDefault="qualified" targetNamespace="http://www.devleap.com/nsordine" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import namespace="http://www.devleap.com/nscliente" />
<xs:element name="Ordine" nillable="true" type="tns:clsOrdine" />
<xs:complexType name="clsOrdine" mixed="true">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" xmlns:q1="http://www.devleap.com/nscliente" ref="q1:NomeCliente" />
<xs:element minOccurs="1" maxOccurs="1" name="TipologiaOrdine" type="tns:EnumTipoOrdine" /></xs:sequence>
<xs:attribute name="dtOrdine" type="xs:dateTime" />
<xs:attribute name="idOrdine" type="xs:string" /></xs:complexType>
<xs:simpleType name="EnumTipoOrdine">
<xs:restriction base="xs:string">
<xs:enumeration value="Business To Consumer" />
<xs:enumeration value="Business To Business" /></xs:restriction>
</xs:simpleType>
</xs:schema>
<xs:schema xmlns:tns="http://www.devleap.com/nsordine" elementFormDefault="qualified" targetNamespace="http://www.devleap.com/nsordine" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import namespace="http://www.devleap.com/nscliente" />
<xs:element name="Ordine" nillable="true" type="tns:clsOrdine" />
<xs:complexType name="clsOrdine" mixed="true">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" xmlns:q1="http://www.devleap.com/nscliente" ref="q1:NomeCliente" />
<xs:element minOccurs="1" maxOccurs="1" name="TipologiaOrdine" type="tns:EnumTipoOrdine" /></xs:sequence>
<xs:attribute name="dtOrdine" type="xs:dateTime" />
<xs:attribute name="idOrdine" type="xs:string" /></xs:complexType>
<xs:simpleType name="EnumTipoOrdine">
<xs:restriction base="xs:string">
<xs:enumeration value="Business To Consumer" />
<xs:enumeration value="Business To Business" /></xs:restriction>
</xs:simpleType>
</xs:schema>
Possiamo chiaramente vedere che lo schema contiene un riferimento ad un secondo schema esterno, che discrive il “cliente”. Inoltre possiamo notare che l’enumerazione per migliorare la descrittività dello schema viene replicata, nella sua definizione, all’interno della descrizione dell’ordine. È anche interessante rendersi conto del fatto che i tipi di dati utilizzati dallo schema XSD corrispondo al meglio a quelli indicati nella definizione della classe nel nostro codice sorgente. dtOrdine per esempio è un dateTime, mentre idOrdine è un dato di tipo string, ecc. Sempre con il tool XSD possiamo ottenere il risultato diametralmente opposto a quello appena visto: generare una classe da un XSD. Proviamo a pensare ad una struttura dati XSD come la seguente:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:p="http://www.devleap.com/nspersona" elementFormDefault="qualified" targetNamespace="http://www.devleap.com/nspersona" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Persona" nillable="false" type="p:clsPersona" />
<xs:complexType name="clsPersona" mixed="true">
<xs:sequence>
<xs:element name="nome" minOccurs="0" maxOccurs="1" type="xs:string" />
<xs:element name="cognome" minOccurs="0" maxOccurs="1" type="xs:string" />
<xs:element name="eta" minOccurs="0" maxOccurs="1" type="xs:positiveInteger" />
<xs:element minOccurs="1" maxOccurs="1" name="Sesso" type="p:EnumSesso" /></xs:sequence>
<xs:attribute name="idPersona" type="xs:string" /></xs:complexType>
<xs:simpleType name="EnumSesso">
<xs:restriction base="xs:string">
<xs:enumeration value="Maschio" />
<xs:enumeration value="Femmina" /></xs:restriction>
</xs:simpleType>
</xs:schema>
<xs:schema xmlns:p="http://www.devleap.com/nspersona" elementFormDefault="qualified" targetNamespace="http://www.devleap.com/nspersona" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Persona" nillable="false" type="p:clsPersona" />
<xs:complexType name="clsPersona" mixed="true">
<xs:sequence>
<xs:element name="nome" minOccurs="0" maxOccurs="1" type="xs:string" />
<xs:element name="cognome" minOccurs="0" maxOccurs="1" type="xs:string" />
<xs:element name="eta" minOccurs="0" maxOccurs="1" type="xs:positiveInteger" />
<xs:element minOccurs="1" maxOccurs="1" name="Sesso" type="p:EnumSesso" /></xs:sequence>
<xs:attribute name="idPersona" type="xs:string" /></xs:complexType>
<xs:simpleType name="EnumSesso">
<xs:restriction base="xs:string">
<xs:enumeration value="Maschio" />
<xs:enumeration value="Femmina" /></xs:restriction>
</xs:simpleType>
</xs:schema>
Se invochiamo il tool XSD con l’argomento /classes a riga di comando, quindi:
xsd persona.xsd /classes /l:cs
otterremo la generazione automatica di una classe C# che rappresenta quella struttura dati. Con /l:vb otterremo una classe Visual Basic .Net . Ecco il risultato in C#:
using System.Xml.Serialization;
[XmlType(Namespace="http://www.devleap.it/nspersona")]
[XmlRoot("Persona", Namespace="http://www.devleap.it/nspersona", IsNullable=false)]
public class clsPersona
{
public string nome;
public string cognome;
[XmlElement(DataType="positiveInteger")]
public string eta;
public EnumSesso Sesso;
[XmlText ()]
public string[] Text;
[XmlAttribute()]
public string idPersona;
}
[XmlType(Namespace="http://www.devleap.it/nspersona")]
public enum EnumSesso
{
Maschio,
Femmina
}
Come vediamo non soltanto è stata creata la classe clsPersona con gli attributi corretti per la serializzazione XML, ma è stata anche creata una enumerazione corrispondente ai due possibili valori del Sesso della persona.
Conclusioni
Lo scopo di questo articolo non era valutare se vi sia una tecnica migliore di un’altra. Il mio obiettivo era valutare le tre modalità di serializzazione di oggetti per riuscire a comprendere “quando usare quale”. Alla luce delle considerazioni fatte emerge chiaramente il fatto che il serializzatore binario è sicuramente il più veloce, dal momento che genera contenuto binario che non deve essere interpretato da un parser XML. Ha però il grosso svantaggio di non essere portabile su altre piattaforme, almeno per ora, e di non rappresentare in modo indipendente dal Framework i dati che serializza. Con il serializzatore SOAP possiamo invece descrivere oggetti in un formato fruibile dalla maggior parte delle piattaforme software oggi esistenti. Laddove il serializzatore SOAP ci dovesse “andare stretto” possiamo passare al serializzatore XML, che produce sempre un risultato che è multipiattaforma ma che è maggiormente personalizzabile in termini di struttura dati e descrizione tramite schema XSD delle stesse. Non è da sottovalutare il tool XSD.EXE che rende semplici molti compiti altrimenti estremamente laboriosi. Buona serializzazione di oggetti con .NET ed XML !