MYMONDAY.ai
서비스스튜디오도구데이터·포맷73CSS 생성기19색상·이미지10인코딩·보안10웹·SEO11개발·데브옵스10파일·내보내기18계산기6가이드

JSON을 Rust 구조체로 변환

JSON을 붙여넣으면 serde derive, snake_case 필드, 선택 키용 Option<T>, 원래 키를 복원하는 rename 속성이 붙은 Rust 구조체가 나옵니다.

  • 브라우저에서 처리
  • 가입 없음
  • 무료

도구 작업 공간

예제

옵션

JSON 입력

27줄435byte

결과

⌘/Ctrl + Enter로 복사
결과 · payload.rs
use serde::{Deserialize, Serialize};
use serde_json::Value;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Payload {
    pub id: i64,
    pub status: String,
    pub customer: PayloadCustomer,
    pub items: Vec<PayloadItem>,
    pub total: f64,
    pub shipped_at: String,
    pub tracking: Value,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PayloadCustomer {
    pub id: i64,
    pub name: String,
    pub email: String,
    pub vip: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PayloadItem {
    pub sku: String,
    pub name: String,
    pub qty: i64,
    pub price: f64,
}
타입3
크기632byte

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가 됩니다.

사용 방법

  1. JSON 붙여넣기

    대표적인 응답을 입력창에 붙여넣거나 샘플을 씁니다. 최상위 객체와 배열은 받지만 문자열, 숫자, 불리언 단일 값은 거부됩니다.

  2. 루트 이름과 옵션 정하기

    Payload가 싫다면 "루트 타입 이름"을 바꿉니다. "누락된 필드는 쓰지 않기"를 켜 두면 값이 없는 선택 키가 직렬화 결과에서 빠지고, 끄면 평범한 Option<T> 필드가 됩니다.

  3. 변환 실행

    출력은 use 줄로 시작해 루트 구조체, 이어서 처음 만난 순서대로 중첩 구조체가 나옵니다. 배열 루트라면 pub type Payload = Vec<PayloadItem>;이 가장 먼저 붙습니다.

  4. rename 속성 확인

    #[serde(rename = ...)] 줄은 필드 이름이 키와 다른 경우에만 붙습니다. r#type처럼 쓰는 키워드에는 붙지 않습니다.

  5. 복사 또는 .rs 저장

    코드를 복사하거나 .rs 파일로 받은 뒤 Cargo.toml에 serde = { version = "1", features = ["derive"] }와 serde_json을 추가합니다.

  6. 타입 다듬기

    serde_json::Value를 실제 타입으로 바꾸고, API가 항상 보내는 필드는 Option을 벗기고, 도메인에 맞으면 i64를 u32나 u64로 좁힙니다.

작동 원리

JSON과 Rust 타입의 대응

도구는 숫자 표기를 보존하며 한 위치의 값을 모두 합칩니다. 절댓값 2^63 미만인 정수 표기는 i64, 소수점·지수 표기와 소수 값 및 2^63 이상의 정수는 f64입니다. 따라서 10.0과 1e3은 값이 정수여도 f64입니다.

JSON 값Rust 타입비고
stringString소유 타입이라 라이프타임이 나오지 않습니다
true / falsebool
2^63 미만의 정수 표기i64음수가 없다면 u32나 u64로 좁히세요
소수, 또는 정수와 소수가 섞인 값f64
타입과 null이 함께 있는 값Option<T>빠진 키도 Option<T>가 됩니다
모든 샘플에서 null, 또는 섞인 모양serde_json::ValueValue가 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로 쓰입니다.

실제 예시

중첩 객체와 문자열 Vec
입력
{"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는 가져오지 않습니다.

예약어와 snake_case가 아닌 키
입력
{"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 이름이 됩니다.

null, 빈 배열, 섞인 배열, 중첩 Vec
입력
{"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이 서버로 전송되나요?

아니요. 입력은 브라우저 안에서만 처리됩니다.

마지막 업데이트: