Arquivo

Textos com Etiquetas ‘Serialização’

Melhores posts do ano de 2009

8 de janeiro de 2010

Serialização de objetos em JSON com Ruby on Rails

13 de dezembro de 2009

Em um post anterior mostrei como serializar objetos em JSON utilizando .NET. Agora vamos fazer a mesma coisa com Ruby on Rails.

Esse é o Jason, não JSON.

Vamos utilizar como exemplo uma classe de modelo chamada SomeFake:

class SomeFake < ActiveRecord::Base

end

Utilizando essa migration:

class CreateSomeFakes < ActiveRecord::Migration
  def self.up
    create_table :some_fakes do |t|
      t.string :text
      t.float :value
      t.timestamps
    end
  end

  def self.down
    drop_table :some_fakes
  end
end

No script/console vamos criar uma instância do modelo SomeFake com os seguintes dados:
>> fake = SomeFake.create :text => "I am a sample text.", :value => 150.85
=> #<SomeFake id: 1, text: "I am a sample text.", value: #<BigDecimal:18ac9f0,'0.15085E3',8(12)>, created_at: "2009-12-13 19:43:28", updated_at: "2009-12-13 19:43:28">

Então queremos serializar a variável fake em JSON para obter o seguinte resultado:
{"id":1,"text":"I am a sample text.","value":150.85}

Para fazer isso, vamos chamar o método to_json na variável fake (estou usando o comando print para uma exibição melhor no console do JSON gerado):
>> print fake_json = fake.to_json
"{"some_fake": {"updated_at": "2009-12-13T19:43:28Z", "text": "I am a sample text.", "id": 1, "value": 150.85, "created_at": "2009-12-13T19:43:28Z"}}"

O resultado que obtemos não é exatamente igual ao que estávamos querendo.

Primeiro, o nome do nosso modelo foi serializado como raiz do objeto em JSON. Isso aconteceu porque por padrão em uma aplicação Rails, a opção ActiveRecord::Base.include_root_in_json é configurada para true no arquivo config/initializers/new_rails_defaults.rb. Nós podemos alterar essa opção para false nesse arquivo, o que afeta a serialização em JSON de toda a aplicação, ou podemos alterá-lo no próprio script/console para nossos testes:
>> ActiveRecord::Base.include_root_in_json = false
=> false

Agora nosso objeto serializado fica assim:
>> print fake_json = fake.to_json
"{"updated_at": "2009-12-13T19:43:28Z", "text": "I am a sample text.", "id": 1, "value": 150.85, "created_at": "2009-12-13T19:43:28Z"}"

A segunda diferença é que não queremos que os atributos de timestamps (created_at, updated_at) sejam serializados. Então vamos dizer para o método to_json não serializar esses atributos, utilizando a opção except:
>> print fake_json = fake.to_json(:except => [:created_at, :updated_at])
“{”text”: “I am a sample text.”, “id”: 1, “value”: 150.85}”

Para fazer o inverso, transformar dados em JSON para um objeto, criamos uma nova instância da classe SomeFake e chamamos o método from_json passando a variável fake_json como parâmetro:
>> other_fake = SomeFake.new
=> #<SomeFake id: nil, text: nil, value: nil, created_at: nil, updated_at: nil>

>> other_fake.from_json fake_json
=> #<SomeFake id: nil, text: "I am a sample text.", value: #<BigDecimal:1708a04,'0.15085E3',8(12)>, created_at: nil, updated_at: nil>

Caso você precise serializar objetos em JSON sem os atributos timestamps com frequência, ao invés de sempre passar a opção except para o método to_json, podemos incluir um novo método na classe ActiveRecord::Base que faça a serialização sem esses atributos. Dessa forma, todos os nossos modelos terão essa funcionalidade.

Vamos chamar esse método de to_json_no_timestamps, o qual sua implementação é mostrada abaixo:

class ActiveRecord::Base
  def to_json_no_timestamps(options = {})
    timestamps_options = [:created_at, :updated_at]

    if (options.has_key? :except)
      if (options[:except].class == Array)
        timestamps_options = options[:except] | timestamps_options
      else
        timestamps_options &amp;lt;&amp;lt; options[:except].to_sym unless options[:except].nil?
      end
    end

    options[:except] = timestamps_options

    to_json options
  end
end

E então basta chamar nosso novo método em uma instância de qualquer modelo:
>> fake = SomeFake.first
=> #<SomeFake id: 1, text: "I am a sample text.", value: #<BigDecimal:1712798,'0.15085E3',8(12)>, created_at: "2009-12-13 19:43:28", updated_at: "2009-12-13 19:43:28">

>> print fake_json = fake.to_json_no_timestamps
"{"text": "I am a sample text.", "id": 1, "value": 150.85}"

Arquitetura, Ruby , , , ,

Serialização de objetos em JSON com .NET

23 de outubro de 2009

Serializar e deserializar instâncias de objetos no formato JSON com .NET acaba sendo muito simples.

Esse é o Jason, não JSON.

Vamos utilizar como exemplo a classe abaixo:

public class SomeFakeClass
{
    public int ID { get; set; }

    public string Text { get; set; }

    public decimal Value { get; set; }
}

E criamos uma instância dela:

SomeFakeClass fake = new SomeFakeClass
{
    ID = 123,
    Text = "I am a sample text.",
    Value = 150.85M
};

Então queremos serializar a variável fake em JSON para obter o seguinte resultado:
{"ID":123,"Text":"I am a sample text.","Value":150.85}

A classe DataContractJsonSerializer torna essa tarefa muito fácil. Basta escrever os dados serializados para um Stream usando o método WriteObject e depois recuperar uma string a partir desse Stream:

MemoryStream stream = new MemoryStream();

DataContractJsonSerializer serializer;
serializer = new DataContractJsonSerializer(typeof(SomeFakeClass));
serializer.WriteObject(stream, fake);

string json = return Encoding.Default.GetString(stream.ToArray());

Para fazer o inverso, transformar dados em JSON para um objeto, usamos o método ReadObject passando o Stream que possui os dados serializados:

string json = "{"ID":123,"Text":"I am a sample text.","Value":150.85}";

MemoryStream stream = new MemoryStream(json);

DataContractJsonSerializer serializer;
serializer = new DataContractJsonSerializer(typeof(SomeFakeClass));

SomeFakeClass fake = (SomeFakeClass)serializer.ReadObject(stream);

Um detalhe importante é que a partir do .NET Framework 3.5 Service Pack 1 não há mais a necessidade de decorar a classe que queremos serializar com os atributos DataContract e DataMember, como o exemplo abaixo:

[DataContract]
public class SomeFakeClass
{
    [DataMember]
    public int ID { get; set; }

    [DataMember]
    public string Text { get; set; }

    [DataMember]
    public decimal Value { get; set; }
}

Isso somente se faz necessário se quisermos ter uma controle maior do que será serializado, como por exemplo, serializar uma propriedade com um nome diferente. Na classe abaixo, a propriedade Text é configurada para ser serializada com o nome Message:

[DataContract]
public class SomeFakeClass
{
    [DataMember]
    public int ID { get; set; }

    [DataMember(Name = "Message")]
    public string Text { get; set; }

    [DataMember]
    public decimal Value { get; set; }
}

Os dados serializados em JSON podem ser algo como:
{"ID":123,"Message":"I am a sample text.","Value":150.85}

Para usar a classe DataContractJsonSerializer, que está no namespace System.Runtime.Serialization.Json, é necessário adicionar referências para dois assemblies no seu projeto:

  • System.Runtime.Serialization
  • System.ServiceModel.Web

Agora que já sabemos como serializar e deserializar em JSON, podemos criar um utilitário que faz esse trabalho. O exemplo abaixo utiliza Extensions Methods combinados com Generics:

public static class JsonSerializerExtensions
{
    public static string ToJson<T>(this T obj)
    {
        MemoryStream stream;

        using (stream = new MemoryStream())
        {
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
            serializer.WriteObject(stream, obj);
        }

        return Encoding.Default.GetString(stream.ToArray());
    }

    public static T FromJson<T>(this string json)
    {
        T obj;

        using (MemoryStream stream = new MemoryStream(Encoding.Default.GetBytes(json)))
        {
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
            obj = (T)serializer.ReadObject(stream);
        }

        return obj;
    }
}

E um exemplo de sua utilização:

SomeFakeClass fake = new SomeFakeClass
{
    ID = 123,
    Text = "I am a sample text.",
    Value = 150.85M
};

string fakeJson = fake.ToJson();

SomeFakeClass otherFake = fakeJson.FromJson<SomeFakeClass>();

.
O código-fonte desse utilitário você pode baixar aqui.

.NET, Livros , , ,