Is The Sky Really The Limit? (Site pessoal de Miguel Prego Gaspar)
 
 
Syndication

Categorias
Do site ...

Locations of visitors to this page





Artigos

Artigos actuais

Serialização
sexta-feira, 20 de Junho de 2008 :: 313 Visualizações :: 0 Comentários :: :: Framework

Introdução

"Serialization" consiste em guardar um objecto num elemento de armazenamento (um ficheiro, uma base de dados, um "buffer de memória, etc.), permitindo a sua posterior reutilização, através de um processo de "deserialization", voltando a recriar o objecto na sua forma original. 

A Serialização é um processo chave da plataforma .Net sendo utilizado de forma transparente para funcionalidades tais como por exemplo para enviar um objecto por valor para outra aplicação. 

Um objecto deve ser tornado serializável se se pretender envia-lo para outra aplicação ou grava-lo em disco ou base de dados (Por exemplo para guardar um objecto num objecto de sessão, na camada de apresentação de uma aplicação "web"). 

O conceito de persistência é complementar ao conceito de serialização. Ambos consistem no armazenamento de um objecto, mas o conceito de persistência define que o meio utilizado é persistente (por exemplo um ficheiro) enquanto o conceito de serialização é independente da persistência do meio (por exemplo, na memória RAM).

Conceitos básicos

A "framework" .Net sabe como serializar todos os tipos básicos de dados, incluindo "integers", "strings" e "arrays" numéricos e de "strings", por isso é possível manter estes objectos serializados em objectos de "stream" (ficheiros por exemplo) com um esforço mínimo. O único requisito para esta operação é a criação de um objecto "formatter" adequado.

Um objecto "formatter" é um objecto que implementa o interface "IFormater" definido no namespace "System.Runtime.Serialization.Namespace". É possível ao programador criar um objecto de "formatter" especifico, criando uma classe que implementa este interface. No entanto o mais frequente é utilizar-se um dos objectos que já existem na plataforma .Net.

"Formatter Objects"

A framework disponibiliza os seguintes objectos:   

  • "BinaryFormatter": Definido no namespace "System.RunTime.Serialization.Formatters.Binary" disponibiliza um mecanismo eficiente para guardar objectos num formato binário compacto. Na verdade é a própria representação binária do objecto em memória que é mantida, por isso os processos de "serialization" e "deserialization" são muito rápidos.   
  • "SoapFormatter": definido no namespace "System.RunTime.Serialization.Formatters.Soap", guarda os objectos num formato reconhecível por humanos, em XML, seguindo as especificações SOAP ("Simple Object Access Protocol"). Os processos de serialização de deserialização são relativamente mais lentos que através do "BinaryFormatter", no entanto tem a vantagem de permitir o envio de dados entre aplicações através de HTTP, bem como de permitir a visualização dos dados numa forma compreensível.

"Binary Serialization"

Os métodos chave que todos os objectos "Formatter" suportam são "Serialize" e "Deserialize"

O método "Serialize" requer como argumentos um objecto do tipo "Stream" e o objecto a ser serializado.

Para recuperar o objecto, é utilizado o método "Deserialize" que aceita como único argumento o objecto de "Stream" e retorna o objecto original. Este objecto é devolvido no tipo "Object" por isso tem de ser convertido no tipo original.

'Passo 1 - Guardar num ficheiro 
'Cria um array de inteiros 
Dim Arr() As Integer = {1, 2, 4, 8, 16, 32, 64, 128, 256} 
'Cria um Stream para guardar o objecto 
Using fs As New FileStream("D:\myfile.dat", FileMode.Create) 
    'Cria o "Formatter" binário 
    Dim MyBinaryFormatter As New BinaryFormatter 
    'Copia o objecto para o ficheiro 
    MyBinaryFormatter.Serialize(fs, Arr) 
End Using 

'Passo 2 - Repõe o objecto 
Dim ArrCopia() As Integer 
'le o ficheiro com o objecto 
Using fs2 As New FileStream("D:\myfile.dat", FileMode.Open) 
    Dim MyBinaryFormatter2 As New BinaryFormatter 
    'Deseriliza o conteudo do ficheiro 
    ArrCopia = DirectCast(MyBinaryFormatter2.Deserialize(fs2), Integer()) 
End Using

Exemplo prático: Conjugando o estudo já efectuado sobre genéricos com a serialização, é possível construir um mecanismo genérico para serialização e deserialização de objectos:

Public Sub SerializeToFile(Of T)(ByVal path As String, ByVal obj As T) 
    ' Cria um Stream para guardar o objecto 
    Using fs As New FileStream(path, FileMode.Create) 
        ' Cria o "Formatter" binário 
        Dim bf As New BinaryFormatter() 
        ' Copia o objecto para o ficheiro 
        bf.Serialize(fs, obj) 
    End Using 
End Sub 

Public Function DeserializeFromFile(Of T)(ByVal path As String) As T 
    ' le o ficheiro com o objecto 
    Using fs As New FileStream(path, FileMode.Open) 
        Dim bf As New BinaryFormatter() 
        ' Deseriliza o conteudo do ficheiro 
        Return DirectCast(bf.Deserialize(fs), T) 
    End Using 
End Function

Demo:

"SOAP Serialization"

O "SoapFormatter" esta marcado como obsoleto na "framework" 2.0. Segundo a "Microsoft" deve optar-se sempre que possível pelo "Binary Formatter". Contudo o "Soap Formatter" possui uma capacidade que falta ao "Binary Formatter", a capacidade de serializar os objectos em XML, legível por humanos.

Esta capacidade é muito útil, especialmente durante o tempo de desenvolvimento e "debug". É por isso frequente a utilização deste "formatter" durante o desenvolvimento e passagem para o "Binary Formatter" em produção.

O namespace "System.RunTime.Serialization.Formatters.Soap" pertence ao "assembly" "System.RunTime.Serialization.Formatters.Soap.dll". Este "assembly" não é referenciado automaticamente pelo "visual studio", pelo que é necessário criar a referencia manualmente.

'Cria um hashtable e coloca-lhe alguns dados 
Dim MyHash As New Hashtable 
MyHash.Add("one", 1) 
MyHash.Add("Two", 2) 
MyHash.Add("Three", 3) 
'Cria um SoapSerializer 
Dim MySoapFormatter As New SoapFormatter 
'Guarda a hashtable em disco no formato SOAP 
Using fs As New FileStream("D:\MyHash.xml", FileMode.Create) 
    MySoapFormatter.Serialize(fs, MyHash) 
End Using 
'Recria o objecto utilizando o mesmo SoapFormatter 
Dim MyHashCopy As Hashtable 
Using fs As New FileStream("D:\MyHash.xml", FileMode.Open) 
    MyHashCopy = DirectCast(MySoapFormatter.Deserialize(fs), Hashtable) 
End Using

Demo:

Criação de tipos serializáveis

Para além da serialização de tipos básicos da plataforma, é possível ao programador criar classes serializáveis.

Para criar uma classe serializável apenas é necessário utilizar um atributo especial: "Serializable":

 _ 
Class Person 
    '… 
End Class

Para que a serialização funcione são necessárias duas condições:   

  1. A classe base, caso exista tem de serializável   
  2. Todos os campos da classe tem de ser de tipos serializáveis.   

Atributo "NonSerialized"

O atributo "NonSerialized" indica que um determinado campo de uma classe "serializable" não deve ser serializado. Este atributo deve ser utilizado nos seguintes casos:

Quando uma classe possui um campo de um tipo não serializável (por exemplo o system.windows.form.control), este atributo é obrigatório para que a classe seja serializada.

Este atributo também é obrigatório quando o valor de um campo deixa de ser válido após deserialização (por exemplo apontadores, handles para ficheiros, etc.)

Quando o valor de um campo é facilmente calculável a partir de outros campos, este atributo pode ser utilizado para diminuir a quantidade de informação a serializar.

Private Xpto As Xpto

"Object Graphs"

Um "object graph" é um conjunto de múltiplos objectos com referências entre si.

É possível serializar um "object graph", mesmo que possua referencias circulares.

Dim p1 As New Person("Antonio", "Silva", #1/12/1960#) 
Dim p2 As New Person("João", "Ferreira", #3/6/1962#) 
Dim p3 As New Person("Ana", "Dias", #10/4/1965#) 
'Estabelecer uma referência circular entre duas pessoas 
p2.Spouse = p3 
p3.Spouse = p2 
'Colocar todas as pessoas numa lista de pessoas 
Dim List As New List(Of Person)(New Person() {p1, p2, p3}) 
'Serializa para o disco através dos métodos genéricos do exemplo anterior 
Dim MyGenericSerialization As New GenericSerialization 
MyGenericSerialization.SerializeToFile("D:\Persons.Dat", List) 
'Cria uma segunda lista e obtem os dados 
Dim list2 As List(Of Person) = MyGenericSerialization.DeserializeFromFile(Of List(Of Person))("D:\Persons.Dat")

"Custom Serialization"

Quando é necessária uma solução mais complexa para lidar com questões de "serialization" e "deserialization" torna-se necessário desenvolver uma "Custom Serialization".

Uma "Custom Serialization" pode ser útil em casos como:   

  • Quando é necessário decidir dinamicamente que informação deve ser serializada e mantida.   
  • Quando é necessário executar código quando o objecto é deserializado, provavelmente para recalcular valores que entretanto deixaram de ser válidos.   

Interface IDeserializationCallBack

O caso mais simples de "Custom Serialization" consiste em executar algum código quando o objecto é completamente deserializado.

O exemplo seguinte contempla o seguinte cenário:  

  • Uma classe "person" abre um ficheiro no construtor, e todos os outros métodos da classe dependem deste ficheiro.   
  • Quando o objecto é deserializado, o construtor não é executado novamente, como tal se nada for feito, a aplicação irá falhar. A solução é implementar nesta classe o interface "IDeserializationCallBack".   
  • Este interface possui um único método "OnDeserialization" o qual é invocado pela "framework" quando o objecto é totalmente deserializado.   
  • É importante notar que a framework invoca o método "OnDeserialization" quando todo o "object graph" estiver sido deserializado, o que significa que garantidamente, todos os objecto dependentes do objecto corrente foram iniciados correctamente. Esta informação é importante se os campos a iniciar no método "OnDeserialization" dependerem de outros campos.   


 _ 
Public Class Person 
    Implements IDeserializationCallback 
    Implements IDisposable 
     Private LogStream As FileStream 
    Public FirstName As String 
    Public LastName As String 
    Private Sub OpenLogFile() 
        Dim fileName As String = Me.FirstName & " " & Me.LastName & ".txt" 
        LogStream = New FileStream(fileName, FileMode.OpenOrCreate) 
    End Sub 

    Sub New(ByVal firstName As String, ByVal lastName As String) 
        Me.FirstName = firstName 
        Me.LastName = lastName 
        ' Abre o ficheiro para logging. 
        OpenLogFile() 
    End Sub 
    ' Este método é chamado quando o objecto foi completamente deserialized. 
    Private Sub OnDeserialization(ByVal sender As Object) Implements IDeserializationCallback.OnDeserialization 
        ' ReAbre o ficheiro 
        OpenLogFile() 
    End Sub 
    Public Sub Dispose() Implements IDisposable.Dispose 
        If LogStream IsNot Nothing Then LogStream.Close() 
    End Sub 
End Class

Interface Iserializable

O interface "ISerializable" permite um controlo total sobre ambos os processos de serialização e deserialização.

Este interface útil em casos em que se pretende definir em tempo de execução quais os campos a serializar ou quando se pretende serializar campos num formato diferente do padrão.

O Interface "ISerializable" expõe apenas um método "GetObjectData" e tem a seguinte sintaxe:

Protected Sub GetObjectData(ByVal info As SerializationInfo, ByVal Context As StreamingContext) 
    '… 
End Sub

O método "GetObjectData" é invocado quando o objecto é passado para o método "Serialize" do "formatter object".

O seu propósito é preencher o objecto "SerializationInfo" com toda a informação do objecto a ser serializado.

O código dentro deste método pode examinar a estrutura "StreamingContext" para obter detalhes acerca do processo de serialização (por exemplo para determinar se o objecto está a ser serializado para um ficheiro ou para memória).

A presença do interface "ISerializable" implica a existência de um construtor especial com a estrutura:

Protected Sub New(ByVal info As SerializationInfo, ByVal Context As StreamingContext) 
    '… 
End Sub

A "framework" chama este construtor quando o objecto é deserializado. A não inclusão deste construtor não gera nenhum erro de compilação, no entanto será gerado um erro de "run time" quando o objecto for deserializado.

O "Scope" utilizado para o método "GetObjectData" e para o construtor é crucial e tem de ser:   

  • "Protected": Se a classe pode ser base para outras classe derivadas
  • "Private": Se a classe for "Sealed".   

O scope nunca deve ser "Public", pois sendo "Protected" ou "Private" o tipo tem obrigatóriamente de possuir pelo menos mais um construtor público, para que o tipo possa ser instanciado.

Nota: O objecto "StreamingContext" é um objecto especial que permite associar o objectivo da serialização ao "formatter object" seleccionado. Este objecto é aceite como segundo argumento num construtor "overloaded".

A utilização deste objecto é recomendada e é considerada uma boa prática, pois permite tomar decisões de acordo com o tipo de serialização. Exemplo:

Dim SC As New StreamingContext(StreamingContextStates.file) 
Dim BF As New BinaryFormatter(Nothing, SC)

O argumento do método "GetObjectData" constituído pelo objecto "SerializeInfo" funciona como um dicionário que é preenchido com os valores a serem serializados, através do método "AddValue":

Protected Sub GetObjectData(ByVal info As SerializationInfo, ByVal Context As StreamingContext) Implements ISerializable.GetObjectData 
    'Guarda os campos 
    info.AddValue("FirstName", Me.FirstName) 
    info.AddValue("Lastname", Me.LastName) 
    ' … 
End Sub

Como esperado, o objecto passado para o método "AddValue" tem de ser "serializable"

Os valores são obtidos posteriormente através do método "GetValue":

Protected Sub New(ByVal info As SerializationInfo, ByVal Context As StreamingContext) 
    'Obtem os campos 
    Me.FirstName = CStr(info.GetValue("Firstname", GetType(String))) 
    Me.LastName = info.GetString("LastNAme") 
    ' … 
End Sub
powered by metaPost

Rating

Comentários
Ainda não existem comentários. Seja o primeiro a colocar um!
Clique aqui para colocar um comentário.
 
 
   
ankaa.net@2009