serialization - Qual é a melhor maneira de lidar com controle de versão usando o protocolo JSON?

Translate

Normalmente, estou escrevendo todas as partes do código em C # e, ao escrever protocolos que são serializados, uso o FastSerializer, que serializa / desserializa as classes de forma rápida e eficiente. Também é muito fácil de usar e bastante simples de fazer "versionamento", ou seja, lidar com diferentes versões da serialização. O que eu normalmente uso é assim:

public override void DeserializeOwnedData(SerializationReader reader, object context)
{
    base.DeserializeOwnedData(reader, context);
    byte serializeVersion = reader.ReadByte(); // used to keep what version we are using

    this.CustomerNumber = reader.ReadString();
    this.HomeAddress = reader.ReadString();
    this.ZipCode = reader.ReadString();
    this.HomeCity = reader.ReadString();
    if (serializeVersion > 0)
        this.HomeAddressObj = reader.ReadUInt32();
    if (serializeVersion > 1)
        this.County = reader.ReadString();
    if (serializeVersion > 2)
        this.Muni = reader.ReadString();
    if (serializeVersion > 3)
        this._AvailableCustomers = reader.ReadList<uint>();
}

e

public override void SerializeOwnedData(SerializationWriter writer, object context)
{            
    base.SerializeOwnedData(writer, context);
    byte serializeVersion = 4; 
    writer.Write(serializeVersion);


    writer.Write(CustomerNumber);
    writer.Write(PopulationRegistryNumber);            
    writer.Write(HomeAddress);
    writer.Write(ZipCode);
    writer.Write(HomeCity);
    if (CustomerCards == null)
        CustomerCards = new List<uint>();            
    writer.Write(CustomerCards);
    writer.Write(HomeAddressObj);

    writer.Write(County);

    // v 2
    writer.Write(Muni);

    // v 4
    if (_AvailableCustomers == null)
        _AvailableCustomers = new List<uint>();
    writer.Write(_AvailableCustomers);
}

Portanto, é fácil adicionar coisas novas ou alterar a serialização completamente, se desejar.

No entanto, agora quero usar JSON por razões não relevantes aqui =) Estou usando atualmenteDataContractJsonSerializere agora estou procurando uma maneira de ter a mesma flexibilidade que tenho usando o FastSerializer acima.

Portanto, a questão é; qual é a melhor maneira de criar um protocolo / serialização JSON e poder detalhar a serialização como acima, para que eu não interrompa a serialização só porque outra máquina ainda não atualizou sua versão?

This question and all comments follow the "Attribution Required."

Todas as respostas

Translate

A chave para o controle de versão de JSON é sempre adicionar novas propriedades e nunca remover ou renomear propriedades existentes. Isso é semelhante acomo os buffers de protocolo lidam com o controle de versão.

Por exemplo, se você começou com o seguinte JSON:

{
  "version": "1.0",
  "foo": true
}

E você deseja renomear a propriedade "foo" para "bar", não apenas renomeie. Em vez disso, adicione uma nova propriedade:

{
  "version": "1.1",
  "foo": true,
  "bar": true
}

Como você nunca remove propriedades, os clientes baseados em versões mais antigas continuarão funcionando. A desvantagem desse método é que o JSON pode ficar inchado com o tempo e você precisa continuar mantendo as propriedades antigas.

Também é importante definir claramente seus casos "avançados" para seus clientes. Suponha que você tenha uma propriedade de array chamada "fooList". A propriedade "fooList" pode assumir os seguintes valores possíveis: não existe / indefinido (a propriedade não está fisicamente presente no objeto JSON ou existe e está definida como "indefinido"), nulo, lista vazia ou uma lista com um ou mais valores. É importante que os clientes entendam como se comportar, especialmente nos casos indefinidos / nulos / vazios.

Eu também recomendaria ler sobre comoversionamento semânticotrabalho. Se você introduzir um esquema de controle de versão semântico para seus números de versão, então alterações compatíveis com versões anteriores podem ser feitas em um limite de versão secundária, enquanto alterações interruptivas podem ser feitas em um limite de versão principal (clientes e servidores teriam que concordar com a mesma versão principal ) Embora não seja uma propriedade do JSON em si, é útil para comunicar os tipos de mudanças que um cliente deve esperar quando a versão muda.

Fonte
Translate

Baseado em java do Googlebiblioteca gsontem um excelente suporte de controle de versão para json. Pode ser muito útil se você estiver pensando em seguir o caminho do java.

Há um tutorial bom e fácilaqui.

Fonte
Translate

Não use DataContractJsonSerializer, como o próprio nome diz, os objetos que são processados por meio desta classe terão que:

a) Ser marcado com os atributos [DataContract] e [DataMember].

b) Estar rigorosamente em conformidade com o “Contrato” definido, ou seja, nada menos e nada mais do que está definido. Qualquer extra ou ausente [DataMember] fará a desserialização para lançar uma exceção.

Se você quiser ser flexível o suficiente, use o JavaScriptSerializer se quiser ir para a opção barata ... ou use esta biblioteca:

http://json.codeplex.com/

Isso lhe dará controle suficiente sobre a serialização JSON.

Imagine que você tenha um objeto em seus primeiros dias.

public class Customer
{ 
    public string Name;

    public string LastName;
}

Depois de serializado, ficará assim:

{Nome: "John", Sobrenome: "Doe"}

Se você alterar a definição do objeto para adicionar / remover campos. A desserialização ocorrerá sem problemas se você usar, por exemplo, JavaScriptSerializer.

public class Customer
{ 
    public string Name;

    public string LastName;

    public int Age;
}

Se você tentar desserializar o último json para esta nova classe, nenhum erro será gerado. O fato é que seus novos campos serão configurados com seus padrões. Neste exemplo: "Idade" será definido como zero.

Você pode incluir, em suas próprias convenções, um campo presente em todos os seus objetos, que contém o número da versão. Neste caso, você pode dizer a diferença entre um campo vazio ou uma inconsistência de versão.

Então, digamos: você tem sua classe Customer v1 serializada:

{ Version: 1, LastName: "Doe", Name: "John" }

Você deseja desserializar em uma instância do cliente v2, você terá:

{ Version: 1, LastName: "Doe", Name: "John", Age: 0}

Você pode, de alguma forma, detectar quais campos em seu objeto são confiáveis e quais não são. Nesse caso, você sabe que sua instância do objeto v2 está vindo de uma instância do objeto v1, portanto, o campo Age não deve ser confiável.

Tenho em mente que você deve usar também um atributo personalizado, por exemplo, "MinVersion", e marcar cada campo com o número de versão mínimo suportado, para obter algo assim:

public class Customer
{ 
    [MinVersion(1)]
    public int Version;

    [MinVersion(1)]
    public string Name;

    [MinVersion(1)]
    public string LastName;

    [MinVersion(2)]
    public int Age;
}

Então, mais tarde, você pode acessar esses metadados e fazer o que for necessário com eles.

Fonte
Translate

Não importa qual protocolo de serialização você usa, as técnicas para criar versões de APIs geralmente são as mesmas.

Geralmente você precisa:

  1. uma forma de o consumidor comunicar ao produtor a versão da API que ele aceita (embora nem sempre seja possível)
  2. uma maneira de o produtor incorporar informações de versão aos dados serializados
  3. uma estratégia compatível com versões anteriores para lidar com campos desconhecidos

Em uma API da web, geralmente a versão da API que o consumidor aceita é incorporada no cabeçalho Aceitar (por exemplo,Accept: application/vnd.myapp-v1+json application/vnd.myapp-v2+jsonsignifica que o consumidor pode lidar com a versão 1 e a versão 2 de sua API) ou menos comumente na URL (por exemplo,https://api.twitter.com/1/statuses/user_timeline.json) Isso geralmente é usado para versões principais (ou seja, alterações incompatíveis com versões anteriores). Se o servidor e o cliente não tiverem um cabeçalho Aceitar correspondente, a comunicação falhará (ou continuará na base de melhor esforço ou retornará para um protocolo de linha de base padrão, dependendo da natureza do aplicativo).

O produtor então gera dados serializados em uma das versões solicitadas e, em seguida, incorpora essas informações de versão aos dados serializados (por exemplo, como um campo denominadoversion) O consumidor deve usar as informações de versão incorporadas aos dados para determinar como analisar os dados serializados. As informações da versão nos dados também devem conter a versão secundária (ou seja, para alterações compatíveis com versões anteriores), geralmente os consumidores devem ser capazes de ignorar as informações da versão secundária e ainda processar os dados corretamente, embora a compreensão da versão secundária possa permitir que o cliente faça suposições adicionais sobre como os dados devem ser processados.

Uma estratégia comum para lidar com campos desconhecidos é como HTML e CSS são analisados. Quando o consumidor vê um campo desconhecido, ele deve ignorá-lo, e quando o dado está faltando um campo que o cliente está esperando, ele deve usar um valor padrão; dependendo da natureza da comunicação, você também pode especificar alguns campos que são obrigatórios (ou seja, campos ausentes são considerados erro fatal). Os campos adicionados em versões secundárias devem ser sempre campos opcionais; a versão secundária pode adicionar campos opcionais ou alterar a semântica dos campos, desde que seja compatível com versões anteriores, enquanto a versão principal pode excluir campos ou adicionar campos obrigatórios ou alterar a semântica dos campos de maneira incompatível com versões anteriores.

Em um formato de serialização extensível (como JSON ou XML), os dados devem ser autodescritivos, ou seja, os nomes dos campos devem ser sempre armazenados junto com os dados; você não deve confiar na disponibilidade de dados específicos sobre posições específicas.

Fonte
Leave a Reply
You must be logged in to post a answer.
Sobre o autor
Ted