Lectura y escritura de JSON en C# (.NET System.Text.Json / Newtonsoft.Json)

En .NET podés trabajar con JSON usando dos enfoques principales:

A continuación verás ambos, con ejemplos equivalentes.

Modelo de ejemplo (POCO / record)

Usaremos esta clase y una lista para los ejemplos:

public class Producto
{
    public int Codigo { get; set; }
    public string Descripcion { get; set; } = "";
    public decimal Precio { get; set; }
    public bool Disponible { get; set; }
    public List<string> Tags { get; set; } = new();
}

// Alternativa moderna
public record ProductoRecord(
    int Codigo,
    string Descripcion,
    decimal Precio,
    bool Disponible,
    List<string> Tags
);

Más sobre records en C#.

A) System.Text.Json (recomendado por defecto)

1) Serializar objeto → JSON

using System.Text.Json;

var prod = new Producto {
    Codigo = 101,
    Descripcion = "Teclado mecánico",
    Precio = 1200.50m,
    Disponible = true,
    Tags = new() { "periférico", "oferta" }
};

string json = JsonSerializer.Serialize(prod);
// {"Codigo":101,"Descripcion":"Teclado mecánico","Precio":1200.50,"Disponible":true,"Tags":["periférico","oferta"]}

2) Deserializar JSON → objeto

var jsonString = """
{
  "Codigo": 102,
  "Descripcion": "Mouse inalámbrico",
  "Precio": 850.75,
  "Disponible": false,
  "Tags": ["periférico"]
}
""";

Producto? prod2 = JsonSerializer.Deserialize<Producto>(jsonString);

3) Opciones útiles (JsonSerializerOptions)

var options = new JsonSerializerOptions
{
    WriteIndented = true,                                 // pretty-print
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,    // camelCase en JSON
    DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
};

string jsonPretty = JsonSerializer.Serialize(prod, options);
// Con CamelCase, la propiedad C# Codigo → "codigo" en el JSON.

4) Atributos (mapear nombres, ignorar propiedades)

using System.Text.Json.Serialization;

public class ProductoApi
{
    [JsonPropertyName("id")]    public int Codigo { get; set; }
    [JsonPropertyName("title")] public string Descripcion { get; set; } = "";
    [JsonIgnore]                 public bool Disponible { get; set; }     // no se serializa
    [JsonInclude]                public List<string> Tags { get; set; } = new();
}

5) Enums y DateTime como strings

using System.Text.Json.Serialization;

var options = new JsonSerializerOptions
{
    Converters = { new JsonStringEnumConverter() },  // enums como strings
    WriteIndented = true
};
// DateTime usa ISO 8601 por defecto; para formato custom, crear un JsonConverter<DateTime> personalizado.

6) Listas y colecciones

var lista = new List<Producto> { prod, prod2! };
string jsonLista = JsonSerializer.Serialize(lista, options);

var lista2 = JsonSerializer.Deserialize<List<Producto>>(jsonLista);

7) Archivos (async)

using System.Text.Json;

await File.WriteAllTextAsync("producto.json", jsonPretty);

using var stream = File.OpenRead("producto.json");
var prod3 = await JsonSerializer.DeserializeAsync<Producto>(stream);

8) Manejo de errores

try
{
    var obj = JsonSerializer.Deserialize<Producto>("{ invalid json ");
}
catch (JsonException ex)
{
    Console.WriteLine($"JSON inválido: {ex.Message}");
}

B) Newtonsoft.Json (Json.NET)

Elegilo si necesitás: LINQ-to-JSON (JObject/JToken), soporte avanzado de conversores, TypeNameHandling, ReferenceLoopHandling, etc.

0) Instalar

dotnet add package Newtonsoft.Json

1) Serializar / deserializar

using Newtonsoft.Json;

var json = JsonConvert.SerializeObject(prod);
// Deserializar
var prod2 = JsonConvert.DeserializeObject<Producto>(json);

2) Formato y opciones

var settings = new JsonSerializerSettings
{
    Formatting = Formatting.Indented,                 // pretty-print
    NullValueHandling = NullValueHandling.Ignore,    // omitir nulls
    Culture = System.Globalization.CultureInfo.InvariantCulture
};
string jsonNice = JsonConvert.SerializeObject(prod, settings);

3) Atributos

using Newtonsoft.Json;

public class ProductoApi2
{
    [JsonProperty("id")]    public int Codigo { get; set; }
    [JsonProperty("title")] public string Descripcion { get; set; } = "";
    [JsonIgnore]             public bool Disponible { get; set; }
}

4) Enums y DateTime

var settings = new JsonSerializerSettings
{
    Formatting = Formatting.Indented,
    Converters = { new Newtonsoft.Json.Converters.StringEnumConverter() } // enum como string
};

// Para DateTime, podés usar DateParseHandling/DateFormatString
settings.DateFormatString = "yyyy-MM-ddTHH:mm:ss.fffK";

5) Listas

var jsonLista = JsonConvert.SerializeObject(lista, settings);
var lista2 = JsonConvert.DeserializeObject<List<Producto>>(jsonLista);

6) Archivos (sincrónico simple)

File.WriteAllText("producto.json", jsonNice);
var data = File.ReadAllText("producto.json");
var prod3 = JsonConvert.DeserializeObject<Producto>(data);

7) LINQ to JSON: JObject / JToken (JSON dinámico)

using Newtonsoft.Json.Linq;

string jsonStr = """
{ "codigo": 101, "extras": { "color": "negro", "stock": 20 } }
""";

JObject obj = JObject.Parse(jsonStr);
int codigo = (int)obj["codigo"]!;
string color = (string?)obj["extras"]?["color"] ?? "N/A";
// Ideal cuando el esquema es dinámico o parcialmente desconocido.

C) Consumir APIs con HttpClient (enviar/recibir JSON)

Con System.Text.Json (helpers)

Usando System.Net.Http.Json:

using System.Net.Http;
using System.Net.Http.Json;  // helpers útiles

var http = new HttpClient { BaseAddress = new Uri("https://miapi.com/") };

var nuevo = new Producto { Codigo = 201, Descripcion = "Monitor 24\"", Precio = 150000m, Disponible = true };

// POST (usa JsonContent y System.Text.Json internamente)
HttpResponseMessage resp = await http.PostAsJsonAsync("productos", nuevo);

if (resp.IsSuccessStatusCode)
{
    // GET parseado a objeto directo
    var productos = await http.GetFromJsonAsync<List<Producto>>("productos");
}

System.Net.Http.Json simplifica mucho: GetFromJsonAsync<T>, PostAsJsonAsync, etc.

Con Newtonsoft (cuando lo necesitás sí o sí)

var http = new HttpClient();
var content = new StringContent(JsonConvert.SerializeObject(nuevo), System.Text.Encoding.UTF8, "application/json");
var resp = await http.PostAsync("https://miapi.com/productos", content);

var body = await resp.Content.ReadAsStringAsync();
var lista = JsonConvert.DeserializeObject<List<Producto>>(body);

D) Diccionarios / dinámicos

// System.Text.Json a Dictionary<string, object?>
var dict = JsonSerializer.Deserialize<Dictionary<string, object?>>("""
{
  "codigo": 101,
  "descripcion": "Teclado",
  "precio": 1200.5
}
""");
// Nota: los valores pueden venir como JsonElement; para escenarios muy dinámicos, JObject de Newtonsoft es más cómodo.

E) Diferencias y elección rápida

Rendimiento: System.Text.Json (muy alto) / Newtonsoft (alto)

Dependencia: Incluido en .NET / Paquete NuGet externo

Opciones: NamingPolicy, IgnoreCondition / NullValueHandling, DefaultValueHandling

Converters: JsonConverter, JsonStringEnumConverter / Converters variados

Dinámico: JsonDocument/JsonNode (limitado) / JObject/JToken (potente)

Ecosistema: Muy bueno / Excelente y maduro

Regla práctica:

  • Usá System.Text.Json por defecto en proyectos modernos.
  • Si necesitás JSON dinámico avanzado o features específicas → Newtonsoft.Json.

F) Buenas prácticas

  • Definí modelos (POCO/record) claramente tipados.
  • Controlá nombres con CamelCase si tu API lo requiere y/o usá atributos (JsonPropertyName / JsonProperty).
  • Ignorá lo que no enviás (JsonIgnore, NullValueHandling.Ignore).
  • Validá antes de deserializar (cuando venga de orígenes externos).
  • Manejá errores (JsonException / JsonReaderException).
  • No guardes dinero como double — usá decimal.
  • Preferí async para E/S (archivos/red) para no bloquear.

G) Mini proyecto de ejemplo (todo junto)

using System.Text.Json;
using System.Text.Json.Serialization;

var opciones = new JsonSerializerOptions
{
    WriteIndented = true,
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};

var productos = new List<Producto>
{
    new() { Codigo = 1, Descripcion = "Teclado", Precio = 1200.50m, Disponible = true, Tags = new(){ "periférico" } },
    new() { Codigo = 2, Descripcion = "Mouse",   Precio = 850.75m,  Disponible = false }
};

// Guardar en archivo
await File.WriteAllTextAsync("productos.json", JsonSerializer.Serialize(productos, opciones));

// Leer de archivo
var jsonLeido = await File.ReadAllTextAsync("productos.json");
var productosCargados = JsonSerializer.Deserialize<List<Producto>>(jsonLeido, opciones);

Console.WriteLine($"Cargados: {productosCargados?.Count}");

Resumen

  • System.Text.Json: rápido, moderno, suficiente para la mayoría de casos.
  • Newtonsoft.Json: ideal si necesitás JObject/JToken, conversores avanzados o compatibilidad con librerías existentes.
  • Dominá serialización/deserialización, opciones, atributos y manejo de errores para trabajar sólido con JSON en C#.