DevToolBox무료
블로그

JSON to C# 클래스: System.Text.Json, Newtonsoft, Records 가이드

17분 읽기by DevToolBox

JSON을 C# 클래스로 변환하는 것은 현대 .NET 개발에서 필수적인 작업입니다. ASP.NET Core Web API, Unity 게임, Blazor 앱, MAUI 모바일 클라이언트 중 무엇을 구축하든 JSON 응답을 역직렬화하기 위해 강력한 타입의 C# 클래스가 필요합니다. 이 가이드는 타입 매핑, System.Text.Json, Newtonsoft.Json, record 타입, POCO 생성, 모범 사례를 다룹니다.

무료 온라인 JSON to C# 클래스 변환기를 사용해 보세요.

JSON to C# 클래스 변환이란?

JSON은 웹 API, 클라우드 서비스, 구성 시스템의 보편적인 데이터 교환 형식입니다. C#은 정적 타입 언어로 명시적 클래스 정의가 필요합니다. JSON to C# 클래스 변환은 JSON 문서를 분석하여 적절한 타입의 속성과 직렬화 특성을 가진 대응하는 C# 클래스를 생성합니다.

일반적인 ASP.NET Core 애플리케이션에서 컨트롤러는 JSON 문자열로 HTTP 요청 본문을 받습니다. 프레임워크는 System.Text.Json이나 Newtonsoft.Json을 사용하여 JSON을 C# 객체로 변환해야 합니다.

Unity, Blazor, Azure Functions에서도 동일한 변환이 필요합니다. 프로세스는 동일합니다: JSON 구조 검사, 타입 결정, 중첩과 배열 처리.

JSON to C#: 타입 매핑

JSON 타입이 C# 타입에 어떻게 매핑되는지 이해하는 것이 변환의 기초입니다:

JSON 타입예시C# 타입참고
string"hello"string항상 System.String
number(정수)42int, longnull 가능시 int?
number(소수)3.14double, decimal금융 데이터에는 decimal
booleantrueboolnull 가능시 bool?
nullnullnullNullable 타입
array[1,2]List<T>List 권장
object{"k":"v"}중첩 클래스강한 타입 클래스 권장

JSON에서 C# 클래스를 생성할 때 값 타입과 Nullable 타입의 선택이 중요합니다. 금액에는 항상 decimal을 사용하세요.

JSON to C# 변환 작동 원리

JSON to C# 클래스 변환기는 체계적인 프로세스를 따릅니다:

  1. JSON 구조 파싱: 구문 트리 구축.
  2. 속성 타입 추론: 각 속성의 C# 타입 결정.
  3. 이름 생성: PascalCase로 변환.
  4. 중첩 객체 처리: 각 중첩 객체가 별도 클래스 생성.
  5. 배열 처리: 요소 타입 분석.
  6. 특성 추가: [JsonPropertyName] 또는 [JsonProperty].
  7. 소스 코드 출력: 포맷된 C# 코드.

코드 예제: System.Text.Json과 Newtonsoft로 JSON to C#

System.Text.Json (.NET 8+): JsonSerializer와 특성

System.Text.Json은 .NET 내장 고성능 JSON 시리얼라이저입니다. .NET 8부터 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와 특성

Newtonsoft.Json은 C# JSON 역직렬화의 사실상 표준 라이브러리입니다. JObject, LINQ-to-JSON, 커스텀 JsonConverter를 제공합니다:

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; }

C# Records: 불변 데이터 모델

C# 9+ records는 불변 데이터 모델을 간결하게 정의합니다. Equals, GetHashCode, 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: 속성이 있는 클래스

완전한 제어가 필요할 때 전통적인 POCO와 INotifyPropertyChanged 또는 AutoMapper가 최대 유연성을 제공합니다:

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);

중첩 JSON 구조 작업

실제 API는 깊게 중첩된 객체와 다형적 타입을 포함합니다:

중첩 객체: 각 중첩 레벨이 별도의 C# 클래스를 생성합니다.

객체 배열: "items": [{"id": 1}]은 C#에서 List<Item>으로 매핑됩니다.

다형적 역직렬화: .NET 7+는 [JsonDerivedType][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; }
}

고급 패턴: 소스 제너레이터, AOT, Nullable 참조 타입

.NET 8 System.Text.Json 소스 제너레이터는 컴파일 시간에 직렬화 코드를 생성하여 리플렉션을 제거하고 Native AOT 배포를 가능하게 합니다:

// .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);
});

C# 8+의 Nullable 참조 타입은 컴파일 시간 null 안전성을 제공합니다. C# 11+의 required 속성과 결합:

// 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은 2-5배 빠르고, 메모리 사용량이 적으며, 소스 제너레이터를 지원합니다. Newtonsoft는 더 많은 기능을 제공합니다.

JSON to C# 변환 모범 사례

견고한 .NET 애플리케이션 구축을 위한 모범 사례:

PascalCase 속성과 특성: [JsonPropertyName]으로 JSON 키 매핑.

Nullable 타입 활성화: #nullable enable으로 컴파일 시간 null 안전.

알 수 없는 속성 처리: System.Text.Json은 기본적으로 무시. Newtonsoft는 MissingMemberHandling.Ignore 설정.

금액에는 decimal: 금액에 float이나 double 사용 금지.

DTO에는 records 우선: init-only 속성의 records로 불변성.

성능을 위한 소스 제너레이터: .NET 8+에서 [JsonSerializable].

역직렬화 데이터 검증: 데이터 어노테이션 또는 FluentValidation.

관련 도구: JSON to Java, JSON to TypeScript, JSON to Kotlin.

JSON to JavaJSON to TypeScriptJSON to Kotlin

자주 묻는 질문

System.Text.Json과 Newtonsoft 중 어떤 것을 사용해야 하나요?

새로운 .NET 8+ 프로젝트에는 System.Text.Json이 권장됩니다. 프레임워크 내장이며, 2-5배 빠르고, AOT 소스 제너레이터를 지원합니다. Newtonsoft는 기존 프로젝트나 JObject가 필요한 복잡한 시나리오에 유용합니다.

C# Records와 전통적인 클래스 중 무엇을 사용해야 하나요?

불변 DTO에는 Records(C# 9+)를 사용하세요. 가변성이나 INotifyPropertyChanged가 필요하면 클래스. ASP.NET Core에서는 Records가 모던한 모범 사례입니다.

알 수 없는 JSON 속성을 어떻게 처리하나요?

System.Text.Json은 기본적으로 무시합니다. 캡처하려면 [JsonExtensionData]를 사용하세요. Newtonsoft는 기본적으로 예외를 던지므로 MissingMemberHandling.Ignore를 설정하세요.

JSON에서 C# 클래스로의 변환은 모든 .NET 개발자의 기본 기술입니다. 무료 온라인 도구로 즉시 코드를 생성하세요.

무료 온라인 도구로 JSON을 C# 클래스로 즉시 변환하세요.

𝕏 Twitterin LinkedIn
도움이 되었나요?

최신 소식 받기

주간 개발 팁과 새 도구 알림을 받으세요.

스팸 없음. 언제든 구독 해지 가능.

Try These Related Tools

C#JSON to C#JVJSON to Java ClassTSJSON to TypeScript{ }JSON Formatter

Related Articles

JSON to Java 클래스 변환기: POJO, Jackson, Gson, Lombok 가이드

JSON을 Java 클래스로 온라인 변환. Jackson, Gson, Lombok으로 POJO 생성 방법을 코드 예제와 함께 알아보세요.

JSON에서 TypeScript로: 예제와 함께하는 완벽 가이드

JSON 데이터를 TypeScript 인터페이스로 자동 변환하는 방법을 배웁니다. 중첩 객체, 배열, 선택적 필드, 모범 사례를 다룹니다.

JSON to Kotlin 데이터 클래스: kotlinx.serialization, Moshi, Gson 가이드

JSON을 Kotlin 데이터 클래스로 온라인 변환. kotlinx.serialization, Moshi, Gson을 사용한 JSON 파싱 방법을 알아보세요.