.Net Xml Serialization, Parte 2

Vimos anteriormente os princípios básicos da Serialização em Xml, as classes necessárias para definir a estrutura do Xml, as que são necessárias para a serialização propriamente dita, e finalmente como estruturar uma classe para que possa ver serializável.

Vamos agora ver em mais detalhe a serialização de objectos de e para disco, os utilitário necessários para criar objectos a partir de um Xml Schema (XSD) e vice-versa. Inicialmente para o exemplo apresentado, foi criado uma string com o Xml resultante da serialização, este Xml está de acordo com o que foi especificado na classe, ou seja é na classe e através de atributos que se define entre outras coisas o seguinte:

  • Quais as propriedades a serem serializadas.
  • Que propriedade são atributos ou elementos do xml.
  • Quais os seus nomes a atribuir a essas propriedades ou elementos.
  • Que elementos e atributos são obrigatórios.

Uma vez não temos um Xml Schema, não é possível verificar à priori se o Xml produzido está de acordo com uma especificação, nesta situação a única forma de validarmos essa conformidade é recorrendo a um bloco try\catch no momento em que o objecto é instanciado a partir do xml, porém esta solução está longe de ser a ideal, uma vez que o Xml pode e deve ser validado a partir de um Schema.
Mesmo ao nível do próprio processo de desenvolvimento, pode ser mais útil criar a definição de uma classe a partir de um Xml Schema existente, uma vez que a maioria dos processos de negócio de hoje implicam a troca de documentos em formato Xml, podemos olhar para o Schema na sua forma mais simplista como o contrato a que esses documentos devem obedecer, pelo que é de todo o interesse que seja o Schema a servir de ponto de partida para criar as classes utilizadas na execução desses processos de negócio.

Para este exemplo vamos usar a “xsd.exe” tool, esta tool faz parte do SDK da.Net Framework, e permite como referido anteriormente criar um XSD Schema a partir de uma classe ou conjunto de classes, e também o inverso, criar a classe ou classes a partir do XSD schema, existem outra ferramentas para este efeito algumas das quais bastante mais poderosas, mas pela sua simplicidade e uma vez que já vem com a Framework é a eleita para este exemplo.

Vejamos as seguintes classes, tal como os nomes indicam a classe SalesOrder representa o cabeçalho de uma encomenda e a classe SalesOrderItem uma linha dos itens dessa mesma encomenda:

Para criar um XSD Schema a partir de uma classe é necessário identificar em primeiro lugar o tipo do objecto, esse é o tipo a partir do qual o Schema vai ser gerado, é também necessário indicar qual a assembly onde está definido esse tipo, para tal usamos o seguinte comando:

xsd.exe -t:SalesOrder SerializationSampleTwo.exe

Este comando vai dar origem a um ficheiro chamado schema0.xsd, esse ficheiro contém a especificação do Schema a que o Xml produzido pela serialização das classes SalesOrder e SalesOrderItem deve obedecer:


Como se pode ver existem propriedades de ambas as classes que estão definidas como atributos e também propriedade que tem nomes diferentes entre a classe e o schema, de acordo com a definição criada pelos diversos atributos na classe. Estes atributos fazem parte do namespace System.Xml.Serialization.

Agora o inverso, partir de um XSD schema e gerar as classes. Para criar um schema pode-se utilizar o Visual Studio, ou o Xml Spy, este ultimo disponibiliza á algum tempo uma versão gratuita, o seu download pode ser feito a partir do seguinte url: http://www.altova.com/support_freexmlspyhome.asp.

O schema que vamos usar para criar as classes é o seguinte:

Para criar as classes a partir de um schema, basta indicar qual o nome do schema, existem mais parâmetros que podem ser passados, tais como o namespace ou a linguagem, neste exemplo vamos usar o mais básico possível, o comando é o seguinte:

xsd.exe /classes schema.xsd

O Visual Studio 2005, automaticamente cria as classes a partir de um schema, para tal basta que se adicione o schema ao projecto, que ele de imediato cria as classes respectivas.

As classes criadas são as seguintes:

Um primeiro reparo, o elemento Items que no schema está definido como uma sequência de elementos CatalogItems é criado como um Array.

   49     public partial class CatalogItems

   50     {

   51         private CalalogItem[] calalogItemField;

   52 

   53         /// <remarks/>

   54         [XmlElement("CalalogItem")]

   55         public CalalogItem[] CalalogItem

   56         {

   57             get { return calalogItemField; }

   58             set { calalogItemField = value; }

   59         }

   60     }

Se o tipo de operações a fazer com os dados envolve apenas leitura, o Array serve perfeitamente, porém o que acontece na maior parte das vezes, é a necessidade de retirar e\ou adicionar elementos, para isso o melhor é utilizar uma generic Collection ao invés de um Array, deste modo temos a facilidade de retirar e adicionar elementos sem a preocupação de estar permanentemente a redimensionar o Array.

   17     public partial class CatalogItems

   18     {

   19         private Collection<CalalogItem> calalogItemField;

   20 

   21         /// <summary>

   22         /// Initializes a new instance of the <see cref="CatalogItems"/> class.

   23         /// </summary>

   24         public CatalogItems()

   25         {

   26             calalogItemField = new Collection<CalalogItem>();

   27         }

   28 

   29         /// <remarks/>

   30         [XmlElement("CalalogItem")]

   31         public Collection<CalalogItem> CalalogItem

   32         {

   33             get { return calalogItemField; }

   34             set { calalogItemField = value; }

   35         }

   36 

   37         /// <summary>

   38         /// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.

   39         /// </summary>

   40         /// <returns>

   41         /// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.

   42         /// </returns>

   43         public override string ToString ()

   44         {

   45             return calalogItemField.Count.ToString();

   46         }

   47     }

Outro reparo a fazer, é o facto de todas as classes estarem num único ficheiro, mas isso não é nada que o ReSharper não possa resolver ;).

Há que ter também em atenção o seguinte, por defeito na classe Catalog a collection de Items (CatalogItems) está com o valor null, esta situação pode provocar algums problemas caso se queira utilizar os items sem que exista uma instancia da collection, o melhor é criar desde logo no construtir da classe Catalog a instancia dos items, deste modo é possível desde logo adicionar ou remover os mesmos. Outros elementos que também não estão instanciados são o Validity e o Factory, pelo que aqui o reparo é o mesmo, contudo a decisão de inicializar estes elementos depende do modo como o developer os vai tratar, ou da definição das regras de negócio.

   38         public Catalog()

   39         {

   40             itemsField = new CatalogItems();

   41             validityField = new Validity();

   42             factoryField = "Sample";

   43         }

Além da ferramenta standard da Framework existem no mercado uma série de outras ferramentas para este mesmo efeito, uma delas pode inclusive ser obtida a partir da Microsoft, o seu nome é XSDObjectGen, é uma versão melhorada da ferramenta standard, mas uma rápida perquisa no Google por “xsd schema generator” vai retornar bastantes ferramentas.

A class utilizada neste exemplo para a serialização permitem gravar uma instancica de um objecto para um ficheiro xml, e criar uma instancia desse objecto a partir de um ficheiro xml.

    7     public static class FileSerializer

    8     {

    9         #region Public Methods

   10 

   11         /// <summary>

   12         /// Serializes the specified source.

   13         /// </summary>

   14         /// <param name="source">The source.</param>

   15         /// <param name="fileName">Name of the file.</param>

   16         public static void Serialize(object source, string fileName)

   17         {

   18             // Create the xml serializer, the serializer needs to know the type

   19             // of the object that will be serialized

   20             XmlSerializer xmlSerializer = new XmlSerializer(source.GetType());

   21 

   22             // Create a XmlTextWriter to write the xml object source, we are going

   23             // to define the encoding in the constructor

   24             using (XmlTextWriter writer = new XmlTextWriter(fileName, Settings.Encoding))

   25             {

   26                 // Save the state of the object into the stream

   27                 xmlSerializer.Serialize(writer, source);

   28                 // Flush the stream

   29                 writer.Flush();

   30             }

   31         }

   32 

   33         /// <summary>

   34         /// Deserializes the specified object string.

   35         /// </summary>

   36         /// <param name="fileName">Name of the file.</param>

   37         /// <param name="type">Type of the object.</param>

   38         /// <returns></returns>

   39         public static object Deserialize(string fileName, Type type)

   40         {

   41             // The object to be returned

   42             object source;

   43 

   44             using (XmlTextReader reader = new XmlTextReader(fileName))

   45             {

   46                 // Create the serializer, we must provide the object type to the constructor

   47                 XmlSerializer xmlSerializer = new XmlSerializer(type);

   48                 // Deserialize the object

   49                 source = xmlSerializer.Deserialize(reader);

   50             }

   51             // Return the new object

   52             return source;

   53         }

   54 

   55         #endregion

   56     }

O aspecto final é este:

De uma forma muito simples é possivel editar o conteúdo do objecto Catalog e dos seus items, gravar e abrir o ficheiro Xml resultante da serialização, ou seja podemos persistir o objecto para um ficheiro e recriar esse mesmo objecto a partir desse ficheiro gravado anteriormente. Esta técnica é particularmente útil para transferir um objecto, ou para por exemplo gravar o estado de um objecto depois de uma mensagem de erro, de modo a permitir a analise do seu conteúdo e o porquê desse erro, ao persistir o objecto para um ficheiro é ainda possível utilizar esse mesmo ficheiro num processo de integração recorrendo por exemplo ao Biztalk Server, mais uma vez a utilização da serialização depende apenas das necessidades e imaginação de cada um.

Published 29-10-2006 3:30 por Pedro Rui Silva
Filed under:

Leave a Comment

(requerido) 
(requerido) 
(opcional)
(requerido) 
If you can't read this number refresh your screen
Enter the numbers above: