Serialização e Desserialização de JSON em C#: Convertendo Objetos e Strings

Abordagens Nativas para Manpiulação de JSON no .NET

A conversão entre objetos C# e strings JSON é uma operação fundamental no desenvolvimento de software, especialmente na comunicação entre APIs. No .NET Framework, existem diferentes classes nativas para realizar essa tarefa. Abaixo, exploramos duas técnicas principais, comparando suas implementações, rigidez de parsing e cenários de uso.

1. Utilizando DataContractJsonSerializer

Esta classe faz parte do namespace System.Runtime.Serialization.Json e exige a referência ao assembly System.Runtime.Serialization.dll. Ela é estritamente compatível com o padrão RFC para JSON, o que significa que não tolera desvios como o uso de aspas simples no lugar de aspas duplas.

Para centralizar a lógica, podemos criar uma classe utilitária genérica que gerencia o fluxo de memória de forma segura:

using System;
using System.IO;
using System.Text;
using System.Runtime.Serialization.Json;

public static class DataContractJsonUtility
{
    public static string Serialize<T>(T entity)
    {
        var serializer = new DataContractJsonSerializer(typeof(T));
        using (var memoryStream = new MemoryStream())
        {
            serializer.WriteObject(memoryStream, entity);
            return Encoding.UTF8.GetString(memoryStream.ToArray());
        }
    }

    public static T Deserialize<T>(string jsonPayload)
    {
        var serializer = new DataContractJsonSerializer(typeof(T));
        using (var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(jsonPayload)))
        {
            return (T)serializer.ReadObject(memoryStream);
        }
    }
}

2. Utilizando JavaScriptSerializer

Disponível no namespace System.Web.Script.Serialization (requer a referência System.Web.Extensions.dll), esta classe é frequentemente preferida em legados por ser mais tolerante. Diferente da abordagem anterior, ela consegue processar strings JSON malformadas que utilizam aspas simples, evitando exceções de parsing em cenários onde o cliente não segue estritamente a especifiacção.

using System.Web.Script.Serialization;

// Instanciação direta
var jsSerializer = new JavaScriptSerializer();

// Desserialização
var objetoConvertido = jsSerializer.Deserialize<MinhaClasse>(jsonString);

// Serialização
string jsonGerado = jsSerializer.Serialize(objetoConvertido);

Modelos de Dados para Teste

Para demonstrar a conversão, definiremos modelos que representam dados de telemetria de uma estação de montagem industrial (MES).

using System.Collections.Generic;

public class MeasurementDetail
{
    public string MetricId { get; set; } = "1";
    public string Parameter { get; set; } = "Torque";
    public string Reading { get; set; } = "25.01";
}

public class AssemblyStationRecord
{
    public string StepCode { get; set; } = "20";
    public string StationId { get; set; } = "N20S1";
    public string SerialNumber { get; set; } = "D:M201796819474";
    public string OperatorId { get; set; } = string.Empty;
    public string OperatorName { get; set; } = string.Empty;
    
    public List<MeasurementDetail> Metrics { get; set; } = new List<MeasurementDetail>
    {
        new MeasurementDetail(),
        new MeasurementDetail { MetricId = "2", Parameter = "Angle", Reading = "88.0" },
        new MeasurementDetail { MetricId = "3", Parameter = "Angle", Reading = "89.0" },
        new MeasurementDetail { MetricId = "4", Parameter = "TorqueMax", Reading = "84.0" }
    };
}

Execução e Comparação de Comportamento

O teste abaixo ilustra a diferença de tolerância entre os dois serializadores ao lidar com uma string JSON contendo aspas simples.

private void ExecuteSerializationTests()
{
    // JSON com aspas simples (inválido pelo RFC 8259, mas comum em sistemas legados)
    string rawJson = @"{ 
        'Metrics':[{'MetricId':'1','Reading':'25.01','Parameter':'Torque'}], 
        'StepCode':'20', 
        'StationId':'N20S1' 
    }";

    // FALHA: DataContractJsonSerializer lançará exceção exigindo aspas duplas.
    // var recordStrict = DataContractJsonUtility.Deserialize<AssemblyStationRecord>(rawJson);

    // SUCESSO: JavaScriptSerializer processa as aspas simples sem erros.
    var jsSerializer = new JavaScriptSerializer();
    var recordLenient = jsSerializer.Deserialize<AssemblyStationRecord>(rawJson);

    // Serializando o objeto de volta para JSON (gerará aspas duplas padrão)
    string outputJson = jsSerializer.Serialize(recordLenient);
}

Aplicação Prática em Comunicação HTTP

Cliente: Enviando Requisições POST

No lado do cliente, o objeto é serializado e transmitido no corpo de uma requisição HTTP. A utilização de HttpClient é a prática recomendada para operações assíncronas.

using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;

private async Task SendTelemetryAsync(string endpoint, AssemblyStationRecord telemetry)
{
    var jsSerializer = new JavaScriptSerializer();
    string jsonBody = jsSerializer.Serialize(telemetry);

    using (var httpClient = new HttpClient())
    {
        var content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
        var response = await httpClient.PostAsync(endpoint, content);
        
        if (response.IsSuccessStatusCode)
        {
            // Lógica de sucesso
        }
    }
}

Servidor: Recebendo e Processando o Payload

No lado do servidor, utilizando HttpListener, o fluxo de entrada é lido, decodificado e desserializado para acionar a lógica de negócios.

using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using System.Web.Script.Serialization;

private async Task ProcessIncomingRequest(HttpListenerContext context)
{
    var request = context.Request;
    var response = context.Response;

    try
    {
        string payload;
        using (var reader = new StreamReader(request.InputStream, request.ContentEncoding))
        {
            payload = await reader.ReadToEndAsync();
        }

        var jsSerializer = new JavaScriptSerializer();
        var incomingData = jsSerializer.Deserialize<AssemblyStationRecord>(payload);

        // Disparar evento ou lógica de negócios com os dados recebidos
        OnTelemetryReceived(incomingData);

        response.StatusCode = 200;
        response.StatusDescription = "OK";
    }
    catch (Exception ex)
    {
        response.StatusCode = 500;
        response.StatusDescription = "Internal Server Error";
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine($"Erro ao processar requisição: {ex.Message}");
    }
    finally
    {
        response.Close();
    }
}

Tags: C# JSON serialização DataContractJsonSerializer JavaScriptSerializer

Publicado em 7-10 04:44