JSON to Rust 변환기는 샘플 JSON을 읽어 Debug, Clone, Serialize, Deserialize가 derive된 pub struct 선언을 만들고 맨 위에 use serde::{Deserialize, Serialize}; 줄을 붙입니다. 중첩 객체는 별도 구조체가 되고 키는 snake_case 필드가 되며, 러스트 이름이 JSON 키와 다르면 #[serde(rename = "...")] 속성이 붙습니다.
같은 위치에 있는 객체는 합쳐집니다. 객체 배열이면 모든 원소의 키를 합친 구조체 하나가 만들어지고, 일부 원소에 없거나 일부에서 null인 키는 Option<T>가 됩니다. 기본적으로 빠진 적 있는 키에는 #[serde(default, skip_serializing_if = "Option::is_none")]가 붙으며 "누락된 필드는 쓰지 않기" 옵션으로 끌 수 있습니다. 루트는 객체와 배열 모두 가능하고 "루트 타입 이름"(기본값 Payload)이 이름을 정합니다.
변환은 브라우저 안에서 이루어지고 입력한 내용은 서버로 전송되지 않습니다. 입력 한도는 20 MiB입니다. 타입은 샘플만 보고 정하므로 샘플 하나로는 키가 항상 있다는 것을 증명할 수 없고, 모든 샘플에서 null인 키는 타입 정보가 없어 serde_json::Value가 됩니다.
사용 방법
JSON 붙여넣기
대표적인 응답을 입력창에 붙여넣거나 샘플을 씁니다. 최상위 객체와 배열은 받지만 문자열, 숫자, 불리언 단일 값은 거부됩니다.
루트 이름과 옵션 정하기
Payload가 싫다면 "루트 타입 이름"을 바꿉니다. "누락된 필드는 쓰지 않기"를 켜 두면 값이 없는 선택 키가 직렬화 결과에서 빠지고, 끄면 평범한Option<T>필드가 됩니다.변환 실행
출력은
use줄로 시작해 루트 구조체, 이어서 처음 만난 순서대로 중첩 구조체가 나옵니다. 배열 루트라면pub type Payload = Vec<PayloadItem>;이 가장 먼저 붙습니다.rename 속성 확인
#[serde(rename = ...)]줄은 필드 이름이 키와 다른 경우에만 붙습니다.r#type처럼 쓰는 키워드에는 붙지 않습니다.복사 또는 .rs 저장
코드를 복사하거나
.rs파일로 받은 뒤 Cargo.toml에serde = { version = "1", features = ["derive"] }와serde_json을 추가합니다.타입 다듬기
serde_json::Value를 실제 타입으로 바꾸고, API가 항상 보내는 필드는Option을 벗기고, 도메인에 맞으면i64를u32나u64로 좁힙니다.
작동 원리
JSON과 Rust 타입의 대응
도구는 숫자 표기를 보존하며 한 위치의 값을 모두 합칩니다. 절댓값 2^63 미만인 정수 표기는 i64, 소수점·지수 표기와 소수 값 및 2^63 이상의 정수는 f64입니다. 따라서 10.0과 1e3은 값이 정수여도 f64입니다.
| JSON 값 | Rust 타입 | 비고 |
|---|---|---|
| string | String | 소유 타입이라 라이프타임이 나오지 않습니다 |
| true / false | bool | |
| 2^63 미만의 정수 표기 | i64 | 음수가 없다면 u32나 u64로 좁히세요 |
| 소수, 또는 정수와 소수가 섞인 값 | f64 | |
| 타입과 null이 함께 있는 값 | Option<T> | 빠진 키도 Option<T>가 됩니다 |
| 모든 샘플에서 null, 또는 섞인 모양 | serde_json::Value | Value가 null을 담을 수 있어 키가 항상 있으면 Option으로 감싸지 않습니다 |
| 객체 | pub struct 이름 | derive(Debug, Clone, Serialize, Deserialize) |
| 배열 | Vec<T> | 중첩 배열은 Vec<Vec<i64>> |
| 빈 배열 | Vec<serde_json::Value> | 배울 원소가 없음 |
| 빈 객체 | struct 이름 {} | 필드가 없는 구조체 |
| 최상위 배열 | pub type Payload = Vec<PayloadItem>; | 구조체들보다 앞에 나옴 |
필드 이름과 serde rename
필드 이름은 소문자로 바꾸고 camelCase 경계에 밑줄을 넣으며, 그 밖의 문자 덩어리는 밑줄 하나로 바꾸고 앞뒤 밑줄은 잘라 냅니다. 그래서 createdAt은 created_at, user-agent는 user_agent가 되고 각각 키를 복원하는 rename 속성이 붙습니다. 숫자로 시작하는 키에는 field_ 접두사가 붙어 2fa는 #[serde(rename = "2fa")]와 함께 field_2fa가 되고, 정리한 결과가 같아지는 키는 숫자 접미사가 붙어 user_id, user_id2가 됩니다.
type, fn, match, async, try 같은 러스트 키워드는 원시 식별자(pub r#type: String)가 됩니다. serde는 원시 식별자를 접두사 없는 이름으로 읽으므로 rename이 붙지 않습니다. 원시 식별자로 쓸 수 없는 self, Self, super, crate만 끝 밑줄과 rename이 붙습니다. 구조체 이름은 부모 이름에 키를 붙인 PascalCase로 PayloadUser 같은 형태이고, 배열 원소 구조체는 키의 단수형을 따라 items는 PayloadItem, data는 PayloadDataItem이 됩니다.
pub r#type: String,
#[serde(rename = "self")]
pub self_: i64,
#[serde(rename = "2fa")]
pub field_2fa: bool,serde_json과 함께 쓰기
의존성을 추가한 뒤 let p: Payload = serde_json::from_str(&text)?;로 파싱하고 serde_json::to_string(&p)?로 원래 키를 유지한 채 다시 직렬화합니다. 입력의 알 수 없는 키는 기본적으로 무시됩니다. 키가 빠지면 오류가 나지만, 필드가 Option<T>이면 serde가 None으로 채웁니다.
옵션을 켜 두면 일부 샘플 객체에서 빠진 키에는 skip_serializing_if = "Option::is_none"도 붙어, None은 null로 쓰이지 않고 출력에서 빠집니다. 일부 샘플에서만 null이고 모든 객체에 있던 필드는 평범한 Option<T>라서 null로 쓰입니다.
실제 예시
{"id":42,"user":{"name":"Ada","active":true},"roles":["admin","editor"]}use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Payload {
pub id: i64,
pub user: PayloadUser,
pub roles: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PayloadUser {
pub name: String,
pub active: bool,
}루트 Payload가 먼저, 이어서 PayloadUser가 나오고 모든 구조체에 Serialize, Deserialize가 derive됩니다. 맨 위에 use 줄이 있고 필요한 곳이 없으므로 serde_json::Value는 가져오지 않습니다.
{"type":"user","fn":1,"self":2,"2fa":true,"user-agent":"curl","createdAt":"2026-01-01"}use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Payload {
pub r#type: String,
pub r#fn: i64,
#[serde(rename = "self")]
pub self_: i64,
#[serde(rename = "2fa")]
pub field_2fa: bool,
#[serde(rename = "user-agent")]
pub user_agent: String,
#[serde(rename = "createdAt")]
pub created_at: String,
}type과 fn은 rename 없는 원시 식별자가 됩니다. self는 원시 식별자가 될 수 없어 rename이 붙은 self_가 되고, 2fa는 field_2fa, 나머지는 rename 속성이 붙은 snake_case 이름이 됩니다.
{"deleted_at":null,"tags":[],"mixed":[1,"a"],"grid":[[1,2],[3]],"ratio":0.5,"count":3000000000}use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Payload {
pub deleted_at: Value,
pub tags: Vec<Value>,
pub mixed: Vec<Value>,
pub grid: Vec<Vec<i64>>,
pub ratio: f64,
pub count: i64,
}null만 있는 키, 빈 배열, 섞인 배열은 serde_json::Value가 되고 타입이 같은 중첩 배열은 Vec<Vec<i64>>가 됩니다. serde_json::Value용 use 줄은 여기서만 추가됩니다.
{"userId":5,"user_id":6}use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Payload {
#[serde(rename = "userId")]
pub user_id: i64,
#[serde(rename = "user_id")]
pub user_id2: i64,
}두 키가 모두 user_id로 정리됩니다. 두 번째 필드는 user_id2가 되고 각 필드가 실제 키를 담은 rename 속성을 가지므로 컴파일되고 두 값도 모두 보존됩니다.
[{"id":1,"name":"Ada"},{"id":2,"email":"grace@example.com"}]use serde::{Deserialize, Serialize};
pub type Payload = Vec<PayloadItem>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PayloadItem {
pub id: i64,
pub name: Option<String>,
pub email: Option<String>,
}원소들이 PayloadItem으로 합쳐지고 타입 별칭이 Vec의 이름을 정합니다. 누락된 필드는 쓰지 않기를 끄면 id는 필수 i64로 남고 name과 email이 serde default 속성 없는 평범한 Option<T>가 됩니다.
자주 하는 실수
null만 있는 키는 Value가 됩니다
붙여넣은 모든 객체에서
deleted_at이 null이면 타입을 알 수 없어serde_json::Value가 됩니다. 값이 들어 있는 레코드를 붙여넣거나Option<String>을 직접 쓰세요.샘플 하나로는 키가 항상 있다고 증명할 수 없습니다
붙여넣은 모든 원소에 있는 키는 필수 필드로 생성되고, 그 키가 없는 페이로드는 역직렬화에 실패합니다. 선택 키가 빠진 레코드를 포함해 여러 건을 붙여넣으면 그 키들이
Option<T>가 됩니다.Option<Value>와 null
항상 있는
Value필드는Value::Null이 이미 null을 뜻하므로Option으로 감싸지 않습니다. 일부 객체에서 빠진 키만Option<Value>가 됩니다.2^63 미만의 정수 표기는 i64입니다
절댓값 2^63 미만인 정수 표기는
i64, 2^63 이상은u64가 아니라f64입니다.i64를 넘는 ID를 정확히 보존하려면 문자열로 입력하고 문자열 또는 큰 정수 타입을 쓰세요.큰 정수는 이미 반올림되어 있습니다
타입 추론에서 9007199254740993 같은 ID를 JavaScript 숫자로 바꾸면 9007199254740992로 반올림됩니다. 타입은 그대로
i64지만 생성 코드가 그 자릿수를 되살릴 수 없으므로 정확한 ID는 문자열로 입력하세요.섞인 모양은 타입 정보를 잃습니다
한 키가 어떤 원소에서는 문자열이고 다른 원소에서는 숫자라면
serde_json::Value가 되고, 타입이 섞인 배열도 마찬가지입니다. 이후에는 값을 직접 분기하거나 untagged 열거형을 직접 써야 합니다.
자주 묻는 질문
null이 가능한 필드에 Option<T>를 만들어 주나요?
네. 일부 샘플에서 null이거나 배열의 일부 객체에 없는 키는 Option<T>가 됩니다. 모든 샘플에서 null인 키는 내부 타입을 알 수 없어 serde_json::Value가 됩니다.
누락된 필드는 쓰지 않기 옵션은 무엇을 하나요?
일부 객체에서 빠진 키의 필드에 #[serde(default, skip_serializing_if = "Option::is_none")]를 붙여 직렬화할 때 None이 출력에서 빠지게 합니다. 그 필드를 null로 쓰고 싶다면 끄세요.
정수가 왜 u32가 아니라 i64인가요?
샘플만으로는 음수가 가능한지 알 수 없어 가장 넓은 부호 있는 정수를 고릅니다. API가 허용하면 나중에 좁히세요.
생성된 파일에서 serde를 import해야 하나요?
아니요. 출력이 이미 use serde::{Deserialize, Serialize};로 시작하고 Value 필드가 있으면 use serde_json::Value;도 붙습니다. Cargo.toml에는 두 크레이트가 필요하고 serde에는 derive 기능이 켜져 있어야 합니다.
type 같은 러스트 키워드는 어떻게 되나요?
r#type 같은 원시 식별자가 되고 serde는 이를 type으로 읽습니다. self, Self, super, crate만 끝 밑줄과 rename이 붙습니다.
JSON 배열을 붙여넣어도 되나요?
네. 원소들이 구조체 하나로 합쳐지고 pub type Payload = Vec<PayloadItem>;이 목록 이름을 정합니다. Payload를 바꾸려면 "루트 타입 이름"을 쓰세요.
루트 구조체 이름은 어떻게 바꾸나요?
"루트 타입 이름"을 바꾸면 되고 PayloadUser 같은 중첩 구조체 이름도 거기서 시작합니다.
입력한 JSON이 서버로 전송되나요?
아니요. 입력은 브라우저 안에서만 처리됩니다.
마지막 업데이트:
