DevToolBoxFREE
BlogAdvertise

JSON a Clase C#: Guia System.Text.Json, Newtonsoft y Records

17 min de lecturapor DevToolBox

Convertir JSON a clases C# es una tarea esencial en el desarrollo .NET moderno. Ya sea que construyas una API ASP.NET Core, un juego Unity, una app Blazor o un cliente MAUI, necesitas clases C# fuertemente tipadas para deserializar respuestas JSON. Esta guia cubre mapeo de tipos, System.Text.Json, Newtonsoft.Json, records, generacion POCO y mejores practicas.

Prueba nuestro convertidor gratuito de JSON a C# en linea.

Que es la conversion de JSON a clase C#?

JSON es el formato universal de intercambio de datos para APIs web y servicios en la nube. C# es un lenguaje de tipado estatico que requiere definiciones de clases explicitas. La conversion de JSON a C# analiza un documento JSON y produce clases C# con propiedades tipadas y atributos de serializacion.

En una aplicacion ASP.NET Core tipica, un controlador recibe el cuerpo de solicitud HTTP como cadena JSON. El framework debe convertir ese JSON en objetos C# usando System.Text.Json o Newtonsoft.Json. Un convertidor JSON a C# automatiza la creacion de estos objetos de transferencia de datos.

La misma conversion es esencial en Unity, Blazor y Azure Functions. El proceso es identico: inspeccionar la estructura JSON, determinar tipos, manejar anidamiento y arrays.

JSON a C#: Mapeo de tipos

Entender como los tipos JSON se mapean a tipos C# es la base de cualquier conversion:

Tipo JSONEjemploTipo(s) C#Notas
string"hello"stringSiempre System.String
number (entero)42int, longint? si nullable
number (decimal)3.14double, decimaldecimal para datos financieros
booleantrueboolbool? si nullable
nullnullnullTipos nullable
array[1,2]List<T>List preferido
object{"k":"v"}Clase anidadaClases tipadas preferidas

Al generar clases C# desde JSON, la eleccion entre tipos valor y tipos nullable es importante. Para valores monetarios, usa siempre decimal.

Como funciona la conversion JSON a C#

Un convertidor JSON a C# sigue un proceso sistematico:

  1. Analizar la estructura JSON: Construccion de un arbol sintactico.
  2. Inferir tipos de propiedad: Determinar el tipo C# para cada propiedad.
  3. Generar nombres: Conversion a PascalCase.
  4. Manejar objetos anidados: Cada objeto anidado genera una clase separada.
  5. Manejar arrays: Analisis del tipo de elemento.
  6. Agregar atributos: [JsonPropertyName] o [JsonProperty].
  7. Producir codigo fuente: Codigo C# formateado.

Ejemplos de codigo: JSON a C# con System.Text.Json y Newtonsoft

System.Text.Json (.NET 8+): JsonSerializer y atributos

System.Text.Json es el serializador JSON de alto rendimiento integrado en .NET. Desde .NET 8 con generadores de codigo fuente para compilacion AOT:

// === Sample JSON ===
// {
//   "user_id": 1001,
//   "user_name": "Alice",
//   "email": "alice@example.com",
//   "is_active": true,
//   "balance": 1250.75,
//   "tags": ["admin", "developer"],
//   "address": {
//     "street": "123 Main St",
//     "city": "Springfield",
//     "zip_code": "62704"
//   }
// }

// === C# class with System.Text.Json attributes ===
using System.Text.Json;
using System.Text.Json.Serialization;

public class User
{
    [JsonPropertyName("user_id")]
    public long UserId { get; set; }

    [JsonPropertyName("user_name")]
    public string UserName { get; set; } = "";

    public string Email { get; set; } = "";

    [JsonPropertyName("is_active")]
    public bool IsActive { get; set; }

    public decimal Balance { get; set; }

    public List<string> Tags { get; set; } = new();

    public Address Address { get; set; } = new();
}

public class Address
{
    public string Street { get; set; } = "";
    public string City { get; set; } = "";

    [JsonPropertyName("zip_code")]
    public string ZipCode { get; set; } = "";
}

// === Deserialization with JsonSerializer ===
var options = new JsonSerializerOptions
{
    PropertyNameCaseInsensitive = true,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};

// Single object
User? user = JsonSerializer.Deserialize<User>(jsonString, options);

// List of objects
List<User>? users = JsonSerializer.Deserialize<List<User>>(
    jsonArrayString, options);

// === Custom JsonConverter for special cases ===
public class EpochToDateTimeConverter : JsonConverter<DateTime>
{
    public override DateTime Read(
        ref Utf8JsonReader reader, Type typeToConvert,
        JsonSerializerOptions options)
    {
        return DateTimeOffset.FromUnixTimeSeconds(
            reader.GetInt64()).DateTime;
    }

    public override void Write(
        Utf8JsonWriter writer, DateTime value,
        JsonSerializerOptions options)
    {
        writer.WriteNumberValue(
            new DateTimeOffset(value).ToUnixTimeSeconds());
    }
}

// Usage: [JsonConverter(typeof(EpochToDateTimeConverter))]
// public DateTime CreatedAt { get; set; }

// === .NET 8 Source Generator (AOT-friendly) ===
[JsonSerializable(typeof(User))]
[JsonSerializable(typeof(List<User>))]
public partial class AppJsonContext : JsonSerializerContext { }

// Zero-reflection deserialization:
User? u = JsonSerializer.Deserialize(
    jsonString, AppJsonContext.Default.User);

Newtonsoft.Json: JsonConvert y atributos

Newtonsoft.Json es el estandar de facto para deserializacion JSON en C# con JObject, LINQ-to-JSON y JsonConverter personalizado:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;

public class User
{
    [JsonProperty("user_id")]
    public long UserId { get; set; }

    [JsonProperty("user_name")]
    public string UserName { get; set; } = "";

    public string Email { get; set; } = "";

    [JsonProperty("is_active")]
    public bool IsActive { get; set; }

    public decimal Balance { get; set; }

    public List<string> Tags { get; set; } = new();

    public Address Address { get; set; } = new();
}

// === Deserialization with JsonConvert ===
var settings = new JsonSerializerSettings
{
    MissingMemberHandling = MissingMemberHandling.Ignore,
    NullValueHandling = NullValueHandling.Ignore,
    DateFormatString = "yyyy-MM-ddTHH:mm:ssZ"
};

// Single object
User? user = JsonConvert.DeserializeObject<User>(
    jsonString, settings);

// List of objects
List<User>? users = JsonConvert.DeserializeObject<List<User>>(
    jsonArrayString, settings);

// === Dynamic parsing with JObject ===
JObject obj = JObject.Parse(jsonString);
string? name = (string?)obj["user_name"];
JArray? tags = (JArray?)obj["tags"];
int tagCount = tags?.Count ?? 0;

// LINQ-to-JSON queries
var activeUsers = JArray.Parse(jsonArrayString)
    .Where(u => (bool)u["is_active"]!)
    .Select(u => (string?)u["user_name"])
    .ToList();

// === Custom JsonConverter ===
public class BoolToIntConverter : JsonConverter<bool>
{
    public override bool ReadJson(
        JsonReader reader, Type objectType, bool existingValue,
        bool hasExistingValue, JsonSerializer serializer)
    {
        return Convert.ToInt32(reader.Value) == 1;
    }

    public override void WriteJson(
        JsonWriter writer, bool value,
        JsonSerializer serializer)
    {
        writer.WriteValue(value ? 1 : 0);
    }
}

// Usage: [JsonConverter(typeof(BoolToIntConverter))]
// public bool IsActive { get; set; }

Records C#: Modelos de datos inmutables

Los records C# 9+ ofrecen una forma concisa de definir modelos inmutables con Equals, GetHashCode y expresiones with:

// C# Record classes for JSON deserialization (C# 9+)
using System.Text.Json.Serialization;

public record User(
    [property: JsonPropertyName("user_id")] long UserId,
    [property: JsonPropertyName("user_name")] string UserName,
    string Email,
    [property: JsonPropertyName("is_active")] bool IsActive,
    decimal Balance,
    List<string> Tags,
    Address Address
);

public record Address(
    string Street,
    string City,
    [property: JsonPropertyName("zip_code")] string ZipCode
);

// Deserialization works seamlessly with records
var options = new JsonSerializerOptions
{
    PropertyNameCaseInsensitive = true
};
User? user = JsonSerializer.Deserialize<User>(json, options);

// Records are immutable: use "with" for modified copies
User updated = user! with { Email = "new@example.com" };

// Init-only record class (C# 10+)
public record class Product
{
    public required long Id { get; init; }
    public required string Name { get; init; }
    public required decimal Price { get; init; }
    public bool InStock { get; init; }
    public List<string> Categories { get; init; } = new();
}

// Value-based equality: two records with same data are equal
var p1 = new Product { Id = 1, Name = "Keyboard", Price = 79.99m };
var p2 = new Product { Id = 1, Name = "Keyboard", Price = 79.99m };
Console.WriteLine(p1 == p2);  // True

POCO manual: Clase con propiedades

Para control total, un POCO tradicional con INotifyPropertyChanged o AutoMapper ofrece maxima flexibilidad:

using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text.Json.Serialization;

// POCO with INotifyPropertyChanged for WPF/MAUI data binding
public class Product : INotifyPropertyChanged
{
    private long _id;
    private string _name = "";
    private decimal _price;
    private bool _inStock;
    private List<string> _categories = new();

    [JsonPropertyName("product_id")]
    public long Id
    {
        get => _id;
        set => SetField(ref _id, value);
    }

    public string Name
    {
        get => _name;
        set => SetField(ref _name, value);
    }

    public decimal Price
    {
        get => _price;
        set => SetField(ref _price, value);
    }

    [JsonPropertyName("in_stock")]
    public bool InStock
    {
        get => _inStock;
        set => SetField(ref _inStock, value);
    }

    public List<string> Categories
    {
        get => _categories;
        set => SetField(ref _categories, value);
    }

    // INotifyPropertyChanged implementation
    public event PropertyChangedEventHandler? PropertyChanged;

    private void OnPropertyChanged(
        [CallerMemberName] string? name = null)
    {
        PropertyChanged?.Invoke(
            this, new PropertyChangedEventArgs(name));
    }

    private bool SetField<T>(
        ref T field, T value,
        [CallerMemberName] string? name = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, value))
            return false;
        field = value;
        OnPropertyChanged(name);
        return true;
    }

    // Override ToString for debugging
    public override string ToString()
        => $"Product {{ Id={Id}, Name={Name}, Price={Price} }}";
}

// Usage with AutoMapper (DTO to domain model):
// var config = new MapperConfiguration(cfg =>
//     cfg.CreateMap<ProductDto, Product>());
// var mapper = config.CreateMapper();
// Product product = mapper.Map<Product>(dto);

Trabajar con estructuras JSON anidadas

Las APIs reales contienen objetos profundamente anidados y tipos polimorficos:

Objetos anidados: Cada nivel de anidamiento produce una clase C# separada.

Arrays de objetos: "items": [{"id": 1}] se mapea a List<Item>.

Deserializacion polimorfica: .NET 7+ soporta [JsonDerivedType] y [JsonPolymorphic].

// Polymorphic deserialization with .NET 7+ System.Text.Json
using System.Text.Json.Serialization;

[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")]
[JsonDerivedType(typeof(EmailNotification), "email")]
[JsonDerivedType(typeof(SmsNotification), "sms")]
[JsonDerivedType(typeof(PushNotification), "push")]
public abstract class Notification
{
    public string Type { get; set; } = "";
    public string Message { get; set; } = "";
    public DateTime CreatedAt { get; set; }
}

public class EmailNotification : Notification
{
    public string Recipient { get; set; } = "";
    public string Subject { get; set; } = "";
}

public class SmsNotification : Notification
{
    public string PhoneNumber { get; set; } = "";
}

public class PushNotification : Notification
{
    public string DeviceToken { get; set; } = "";
    public string Title { get; set; } = "";
}

// JSON input:
// {"type":"email","message":"Hello","recipient":"a@b.com","subject":"Hi"}
// Automatically deserializes to EmailNotification

// Nested classes with arrays example
public class ApiResponse
{
    public bool Success { get; set; }
    public UserData Data { get; set; } = new();
    public List<ErrorDetail> Errors { get; set; } = new();
}

public class UserData
{
    public User User { get; set; } = new();
    public List<Order> Orders { get; set; } = new();
    public Address BillingAddress { get; set; } = new();
    public Address ShippingAddress { get; set; } = new();
}

public class Order
{
    public long OrderId { get; set; }
    public decimal Total { get; set; }
    public List<OrderItem> Items { get; set; } = new();
}

public class OrderItem
{
    public long ProductId { get; set; }
    public string Name { get; set; } = "";
    public int Quantity { get; set; }
    public decimal UnitPrice { get; set; }
}

Patrones avanzados: Generadores de codigo, AOT y tipos nullable

Los generadores de codigo .NET 8 para System.Text.Json producen codigo de serializacion en tiempo de compilacion, eliminando reflexion y habilitando despliegue AOT nativo:

// .NET 8 Source Generator for AOT-friendly serialization
using System.Text.Json;
using System.Text.Json.Serialization;

public record Product(
    [property: JsonPropertyName("product_id")] long ProductId,
    string Name,
    decimal Price,
    [property: JsonPropertyName("in_stock")] bool InStock,
    List<string> Tags,
    [property: JsonPropertyName("created_at")] DateTime CreatedAt
);

// Source generator context
[JsonSerializable(typeof(Product))]
[JsonSerializable(typeof(List<Product>))]
[JsonSourceGenerationOptions(
    PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
    GenerationMode = JsonSourceGenerationMode.Default)]
public partial class AppJsonContext : JsonSerializerContext { }

// Zero-reflection serialization (AOT-compatible)
var product = JsonSerializer.Deserialize(
    json, AppJsonContext.Default.Product);
var jsonOut = JsonSerializer.Serialize(
    product, AppJsonContext.Default.Product);

// Works with ASP.NET Core minimal APIs
var builder = WebApplication.CreateBuilder(args);
builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolverChain
        .Insert(0, AppJsonContext.Default);
});

Los tipos referencia nullable C# 8+ proporcionan seguridad null en tiempo de compilacion. Combinados con propiedades required (C# 11+):

// Nullable reference types + required properties (C# 11+)
#nullable enable

public class UserProfile
{
    public required string Name { get; init; }
    public required string Email { get; init; }
    public string? Nickname { get; init; }     // optional
    public int Age { get; init; }
    public string? AvatarUrl { get; init; }     // optional
    public required List<string> Roles { get; init; }
}

// Deserialization enforces required properties
var options = new JsonSerializerOptions
{
    PropertyNameCaseInsensitive = true
};
var user = JsonSerializer.Deserialize<UserProfile>(json, options)
    ?? throw new InvalidOperationException(
        "Failed to deserialize user profile");

// Compiler warning if you forget to set required properties:
// var profile = new UserProfile { Name = "Alice" };
// Error CS9035: Required member 'Email' must be set

// JsonRequired attribute for runtime enforcement
public class StrictModel
{
    [JsonRequired]
    public string Id { get; set; } = "";

    [JsonRequired]
    public string Name { get; set; } = "";

    public string? Description { get; set; }
}

System.Text.Json vs Newtonsoft: System.Text.Json es 2-5x mas rapido, usa menos memoria y soporta generadores de codigo. Newtonsoft ofrece mas funcionalidades.

Mejores practicas para conversion JSON a C#

Sigue estas mejores practicas para aplicaciones .NET robustas:

Propiedades PascalCase con atributos: [JsonPropertyName] para mapear claves JSON.

Activar tipos nullable: #nullable enable para seguridad null en compilacion.

Manejar propiedades desconocidas: System.Text.Json las ignora por defecto. Configurar Newtonsoft con MissingMemberHandling.Ignore.

Decimal para valores monetarios: Nunca usar float o double para dinero.

Preferir records para DTOs: Records con propiedades init-only para inmutabilidad.

Generadores de codigo para rendimiento: [JsonSerializable] en .NET 8+.

Validar datos deserializados: Anotaciones de datos o FluentValidation.

Herramientas relacionadas: JSON a Java, JSON a TypeScript, JSON a Kotlin.

JSON to JavaJSON to TypeScriptJSON to Kotlin

Preguntas frecuentes

System.Text.Json o Newtonsoft: cual debo usar?

System.Text.Json es recomendado para nuevos proyectos .NET 8+. Esta integrado, es 2-5x mas rapido y soporta generadores de codigo AOT. Newtonsoft sigue siendo util para proyectos existentes y escenarios complejos con JObject.

Records C# o clases tradicionales?

Records para DTOs inmutables (C# 9+). Clases para mutabilidad o INotifyPropertyChanged. Para ASP.NET Core, records son la practica moderna.

Como manejar propiedades JSON desconocidas?

System.Text.Json las ignora por defecto. Para capturarlas, usa [JsonExtensionData]. Newtonsoft lanza excepcion por defecto, configura MissingMemberHandling.Ignore.

Convertir JSON a C# es una habilidad fundamental. Usa nuestra herramienta gratuita para generacion instantanea.

Convierte JSON a clases C# al instante con nuestra herramienta gratuita.

¿Fue útil?

Stay Updated

Get weekly dev tips and new tool announcements.

No spam. Unsubscribe anytime.

Partner Picks

Sponsor this article

Place your product next to this developer topic with tracked clicks.

Ask about article sponsorship

This site uses cookies for analytics and to display ads. By continuing to browse, you agree. Privacy Policy