msdn10_capa.JPG

Clique aqui para ler todos os artigos desta edição

 

Serializando objetos com .Net

por Wallace Cézar Sales dos Santos

Um dos grandes desafios do desenvolvimento orientado a objetos é a serialização e desserialização dos objetos. O .Net Framework oferece recursos importantes e facilitadores para a realização dessa tarefa durante o ciclo de vida de um objeto.  A serialização é utilizada porque precisamos, basicamente, persistir o estado dos objetos.

Introdução

Serialização é o processo pelo qual convertemos o estado de um objeto num formato em que possamos persisti-lo ou transportá-lo. Desserialização é o processo inverso, permitindo assim, uma facilidade maior em manipular objetos durante espaços de tempo e limites de aplicação, conforme a necessidade identificada e projetada.

Utilizamos serialização de diversas formas nos sistemas: para armazenar um objeto no banco de dados ou em disco, serializar para compartilhar objetos entre várias aplicações ou ainda, transportar objetos através de uma rede, como nas operações de Remoting. O .Net Framework nos fornece nativamente duas técnicas para serialização de objetos: Serialização binária e Serialização por XML e SOAP.

Por que Serializar?

A primeira coisa que precisamos ter em mente é “Por que queremos serializar um objeto?”. As razões podem ser muitas, porém estarão sempre relacionadas com duas operações: A primeira é armazenar o estado de um objeto em uma cópia exata que será recuperada posteriormente; a segunda razão é transportar o estado do objeto entre domínios de aplicações, que podem ser num mesmo computador, numa intranet ou mesmo na Internet, através de Web Services, por exemplo.

No primeiro caso pensamos imediatamente às operações relacionadas com a sigla CRUD e os bancos de dados: Create, Read, Update e Delete. Afinal, o normal é fazermos exatamente isto: desenhamos métodos dos objetos utilizando bancos de dados para armazenar seu estado. Só que esta atividade pode ser muito difícil de realizar a medida em que a complexidade do objeto aumenta e passamos a não ter mais apenas um objeto e sim vários objetos complexos. A tarefa, até então trivial, ganha exponencial complexidade. Notem, não quero de forma alguma passar a idéia de que isto é errado, porém é importante sabermos que temos opções a este árduo trabalho (determinada através dos requerimentos e da correta arquitetura do software), uma vez que a serialização nos fornece um mecanismo bastante interessante e conveniente para alcançarmos este objetivo, com um esforço substancialmente menor.

Para a segunda opção, é importante termos em mente que um objeto somente é válido no domínio da aplicação que o criou. Se este objeto não for serializável, qualquer tentativa de transferi-lo de um domínio para outro resultará em fracasso. No .Net realizamos as operações de transferência de objetos entre domínios de aplicação através do uso de Remoting, que não é o escopo neste artigo.

Serializando um objeto

A serialização no .Net é um processo relativamente simples. Precisamos apenas marcar a classe desejada como Serializável, que pode ser feito através da adição do atributo à classe desejada, como vemos na Listagem 1. Este atributo, quando adicionado à uma classe, indica que suas instâncias poderão ser serializadas através de operações de Reflection. Para a classe em si, o trabalho está finalizado.

Listagem 1: Marcando uma classe como serializável

Public Class Prancha

   'Adicione aqui o código da Classe

End Class

 

A grande questão vem com a necessidade do uso da serialização. Uma vez identificados os requisitos no sistema onde faremos uso desta tecnologia, cabe-nos realizar as tarefas necessárias para implementá-la no código. A tarefa continua simples, mas agora precisamos tomar alguns cuidados e realizar algumas pequenas tarefas para o completo sucesso da operação. A primeira é referenciar em nossa classe os namespaces que  permitirão o uso de serialização: System.Runtime.Serialization, que contém as classes utilizadas para as operações de serialização e desserialização de objetos. A segunda é definir qual formatador utilizaremos e fazermos a também a declaração do namespace para uso na classe, que pode ser o System.Runtime.Serialization.Formatters.[Binary]ou[Soap]. O formatador define como será armazenado o estado do objeto: o primeiro caso para serialização binária e o segundo em formato XML/SOAP. Cabe ressaltar que se você desejar utilizar a opção com SOAP, deverá fazer uma referência  separada ao Assembly de nome igual antes no projeto (esta classe encontra-se num assembly diferente).

O passo seguinte é criar uma instância do formatador escolhido (uma das opções citadas anteriormente) e uma instância de uma classe derivada da classe System.IO.Stream (FileStream, por exemplo). Esta classe Stream serve para que possamos, neste caso, persistir em disco o estado do objeto. Depois de criadas ambas as intâncias, executamos o método Serialize() do formatador, como podemos verificar na Listagem 2.

Listagem 2: Implementando a serialização

'Criando uma instância do Formatador

Dim pranchaFormatter As IFormatter = New SoapFormatter

'Criando uma instância do Stream

Dim stream As Stream = New FileStream("Prancha.xml", FileMode.Create, FileAccess.Write, FileShare.None)

'Serializando o objeto

pranchaFormatter.Serialize(stream, prancha)

'Fechando o Stream

stream.Close()

 

Note na Listagem 2 que em nenhum momento definimos o que será ou não serializado. É importante ter em mente que o mecanismo de serialização automaticamente armazena o estado de todas as variáveis de classe (campos), desde que definidos como privados. A Listagem 3 demonstra a implementação completa da Classe Prancha e dela somente será serializado as informações armazenadas nos campos da região “Campos internos”. Porém, chamo a atenção para um detalhe: o campo _tipoPrancha está marcado com o atributo , e isso significa que esta informação não será serializada pelo mecanismo. Isto é necessário pois haverá ocasiões onde não desejaremos que o estado de uma determina propriedade seja armazenado, como por exemplo, a senha de um usuário do sistema.

Listagem 3: a classe Prancha

Public Class Prancha

'Adicione aqui o código da Classe

#Region " Campos internos"

    Private _numeroQuilha As NumeroQuilha = NumeroQuilha.TriQuilha

    Private _tamanho As Single = 6.0F

    Private _tipoRabeta As TipoRabeta = TipoRabeta.Squash

    Private _largura As Single = 19.0F

    Private _proprietario As String = String.Empty

    Private _dataFabricacao As DateTime = DateTime.Now

    Private _tipoPrancha As TipoPrancha = TipoPrancha.HotDog

    Private _shaper As Shaper = Nothing

#End Region

 

#Region " Constantes"

    Private Const PRANCHA_PEQUENO As String = "O tamanho informado é muito pequeno."

    Private Const PRANCHA_ESTREITA As String = "A largura informada é muito estreita."

    Private Const PROPRIETARIO_VAZIO As String = "É necessário informar o nome do proprietário da prancha."

    Private Const DATA_INVALIDA As String = "Data informada inválida."

#End Region

 

#Region " Construtores"

    Public Sub New()

        MyBase.New()

    End Sub

 

    Public Sub New(ByVal proprietario As String, ByVal tamanho As String)

        Me.New()

        With Me

            .Proprietario = proprietario

            .Tamanho = tamanho

        End With

    End Sub

#End Region

 

#Region " Propriedades"

    Public Property NumeroQuilha() As NumeroQuilha

        Get

            Return Me._numeroQuilha

        End Get

        Set(ByVal Value As NumeroQuilha)

            Me._numeroQuilha = Value

        End Set

    End Property

 

    Public Property Tamanho() As Single

        Get

            Return Me._tamanho

        End Get

        Set(ByVal Value As Single)

            If (Value < 4.5F) Then Throw New ApplicationException(Me.PRANCHA_PEQUENO)

            Me._tamanho = Value

        End Set

    End Property

 

    Public Property TipoRabeta() As TipoRabeta

        Get

            Return Me._tipoRabeta

        End Get

        Set(ByVal Value As TipoRabeta)

            Me._tipoRabeta = Value

        End Set

    End Property

 

    Public Property Largura() As Single

        Get

            Return Me._largura

        End Get

        Set(ByVal Value As Single)

            If (Value < 18.0F) Then Throw New ApplicationException(Me.PRANCHA_ESTREITA)

            Me._largura = Value

        End Set

    End Property

 

    Public Property Proprietario() As String

        Get

            Return Me._proprietario

        End Get

        Set(ByVal Value As String)

            If ((Value Is String.Empty) OrElse (Value Is Nothing)) Then Throw New ApplicationException(Me.PROPRIETARIO_VAZIO)

            Me._proprietario = Value

        End Set

    End Property

 

    Public Property DataFabricacao() As DateTime

        Get

            Return Me._dataFabricacao

        End Get

        Set(ByVal Value As DateTime)

            If ((Value < DateTime.Today) OrElse (Value = Nothing)) Then Throw New ApplicationException(Me.DATA_INVALIDA)

            Me._dataFabricacao = Value

        End Set

    End Property

 

    Public Property TipoPrancha() As TipoPrancha

        Get

            Return Me._tipoPrancha

        End Get

        Set(ByVal Value As TipoPrancha)

            Me._tipoPrancha = Value

        End Set

    End Property

 

    Public ReadOnly Property Shaper() As Shaper

        Get

            If (Me._shaper Is Nothing) Then Me._shaper = New Shaper

            Return Me._shaper

        End Get

    End Property

 

#End Region

 

End Class

 

Uma vez executado o código apresentado na Listagem 2 sobre a classe apresentada na Listagem 3, teremos como resultado o arquivo Prancha.xml, como vemos na Listagem 4. Um detalhe que é importante observar é o fato da serialização ter ocorrido inclusive para os objetos membros – campo _shaper, que é um tipo Shaper, sem que tenhamos qualquer linha de codificação a mais para realizar esta tarefa.

Listagem 4: Arquivo gerado pelo formatador SoapFormatter