A comunicação entre sistemas é algo recorrente no dia a dia do desenvolvedor. Seja a comunicação com um sistema de terceiro, ou mesmo um sistema interno. Entretanto, atualmente o mais comum é a comunicação entre a aplicação e uma API. E nestas situações, geralmente isso ocorre via JSON.
Existem algumas bibliotecas para se trabalhar com JSON no .NET, sendo que as que mais se destacam são Newtonsoft.Json e System.Text.Json. Neste artigo iremos ver a segunda.
Curso C# (C Sharp) - APIs REST com ASP.NET Web API
Conhecer o cursoTudo começou no .NET Core 2.1
Conforme o ecossistema do .NET Core foi crescendo, o processamento de dados em JSON se tornou uma parte essencial das aplicações da plataforma, principalmente as voltadas para web, como ASP.NET Core e SignalR. Mesmo que fornecesse alguns recursos para trabalhar com JSON, era quase unanime o uso da biblioteca Newtonsoft.Json
para este serviço.
Assim, no .NET Core 2.1 esta biblioteca foi integrada a plataforma e se tornou uma dependência padrão de alguns projetos, como o ASP.NET Core. Algo que foi bem aceito, mesmo que em alguns cenários isso gerasse conflitos e/ou limitações (e.g. não era possível utilizar uma versão da biblioteca não suportada pelo ASP.NET Core 2.1).
Sempre visando melhorar a performance da plataforma, durante o desenvolvimento da versão 3, a equipe da Microsoft notou que a biblioteca Newtonsoft.Json
poderia ser um limitador para o processamento de JSON. Não que esta biblioteca não seja performática, mas as suas limitações poderiam ser um gargalo para este aspecto do .NET Core.
A equipe pensou em atualizá-la, mas isso iria “quebrar” as antigas versões da mesma, além de outras bibliotecas que dependem dela. Desta forma, no .NET Core 3.0 foi introduzido uma nova biblioteca JSON, a System.Text.Json
.
Visando alta performance, esta biblioteca não é equivalente a Newtonsoft.Json
, então não dá para substituir uma pela outra. Em alguns casos, o uso da primeira ainda é recomendado e em caso de dúvidas, a Microsoft fornece uma tabela comparando os recursos de cada uma.
Serialização e Deserialização com System.Text.Json
A biblioteca System.Text.Json
fornece três formas de se trabalhar com JSON:
- JsonSerializer: API de uso geral, utilizada para serializar e desserializar JSON em objetos POCO;
- JsonDocument: API mais avançada, que permite que o JSON seja quebrado e se torne “navegável”;
- Utf8JsonReader: API que dá controle total sobre o JSON e permite que o desenvolvedor decida como cada token deve ser tratado.
O uso de cada uma irá depender do objetivo, entretanto, por ser bem mais avançada, neste artigo não iremos abordar o Utf8JsonReader
. Veremos às demais a seguir.
JsonSerializer
É bem comum necessitar desserializar um JSON em um objeto. Caso este JSON não tenha nenhuma particularidade, isso pode ser feito de forma simples com o método Deserialize
:
using System;
using System.Text.Json;
var json = @"{""Nome"":""Carlos Silva"",""Idade"":33}";
var pessoa = JsonSerializer.Deserialize<Pessoa>(json);
Console.WriteLine(pessoa.Nome);
Console.WriteLine(pessoa.Idade);
public class Pessoa
{
public String Nome { get; set; }
public int Idade { get; set; }
}
Se o JSON definir algo fora do padrão, como um valor numérico como string:
var json = @"{""Nome"":""Carlos Silva"",""Idade"": ""33""}";
Pode ser utilizado o JsonSerializerOptions
para indicar que este valor deve ser contido para numérico:
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
var options = new JsonSerializerOptions
{
NumberHandling = JsonNumberHandling.AllowReadingFromString
};
var json = @"{""Nome"":""Carlos Silva"",""Idade"": ""33""}";
var pessoa = JsonSerializer.Deserialize<Pessoa>(json, options);
Console.WriteLine(pessoa.Nome);
Console.WriteLine(pessoa.Idade);
public class Pessoa
{
public String Nome { get; set; }
public int Idade { get; set; }
}
Se o processo for o inverso, converter um objeto para JSON, pode ser utilizado o método Serialize
:
using System;
using System.Text.Json;
var pessoa = new Pessoa { Nome = "Carlos Silva", Idade = 33 };
var json = JsonSerializer.Serialize(pessoa);
Console.WriteLine(json); // {"Nome":"Carlos Silva","Idade":33}
public class Pessoa
{
public String Nome { get; set; }
public int Idade { get; set; }
}
Este método também aceita opções:
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
var options = new JsonSerializerOptions
{
//Lê valores numéricos definidos como string e escreve valores numéricos como strings
NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString,
//Identa o JSON gerado
WriteIndented = true,
//Ignora propriedades com valor nulo ou padrão
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
};
var pessoa = new Pessoa { Nome = "Carlos Silva", Idade = 33 };
var json = JsonSerializer.Serialize(pessoa, options);
Console.WriteLine(json);
// {
// "Nome": "Carlos Silva",
// "Idade": "33"
// }
public class Pessoa
{
public String Nome { get; set; }
public int Idade { get; set; }
public List<String> Telefones { get; set; }
}
JsonDocument
O JsonDocument
deve ser utilizado para situações onde se possui o JSON, mas não se deseja que seja desserializado para um objeto POCO. Por exemplo, se a intenção for acessar algum elemento dele:
using System;
using System.Text.Json;
var json = @"{""Nome"":""Carlos Silva"",""Idade"":33, ""Telefones"": { ""celular"": ""11-99999-9999"", ""comercial"": ""11-4444-4444""}}";
var obj = JsonDocument.Parse(json);
var nome = obj.RootElement.GetProperty("Nome").GetString();
var celular = obj.RootElement.GetProperty("Telefones").GetProperty("celular").ToString();
Console.WriteLine(nome);//Carlos Silva
Console.WriteLine(celular);//11-99999-9999
Quando se sabe a localização deste elemento, o seu acesso será simples. Entretanto, e se for necessário pesquisar o elemento dentro do JSON? Para estas situações pode ser utilizado os métodos EnumerateObject
e/ou EnumerateArray
, que permitem percorrer o JSON:
using System;
using System.Linq;
// using System.Collections.Generic;
using System.Text.Json;
// using System.Text.Json.Serialization;
var json = @"{""Nome"":""Carlos Silva"",""Idade"":33, ""Telefones"": { ""celular"": ""11-99999-9999"", ""comercial"": ""11-4444-4444""}}";
var obj = JsonDocument.Parse(json);
var telefones = obj.RootElement.EnumerateObject()
.Where(jsonProperty => jsonProperty.Name.Contains("Telefones")
&& jsonProperty.Value.ValueKind == JsonValueKind.Object)
.Select(jsonProperty => jsonProperty.Value.GetProperty("comercial"));
foreach(var telefone in telefones)
Console.WriteLine(telefone);//11-4444-4444
Serialização e desserialização são operações “caras”, que o JsonDocument
procura diminuir mantendo a alocação de memória ao mínimo. Desta forma, ele deve ser utilizado, principalmente quando o JSON for muito complexo para uma classe POCO, for necessário acessar apenas algumas partes dele e/ou não se saiba o formato dos seus dados.
Curso C# (C Sharp) - Introdução ao ASP.NET Core
Conhecer o cursoFinalizando
Deste que foi lançada a System.Text.Json
vem recebendo recursos e melhorias. Ainda não é uma biblioteca substituta para a Newtonsoft.Json
(algo que não é a intenção, então não deve ocorrer). Mas devido a sua performance, sempre que ela atender as suas necessidades, opte pelo seu uso.
Então fico por aqui. Até à próxima.