JSON
개요
JSON 모듈은 가볍고 빠른 JSON 파서 및 생성기를 제공합니다. null, bool, number(i64), string, array, object를 지원하며, 구조체 ↔ JSON 매핑을 통해 직렬화/역직렬화를 구현합니다.
Quick Start
U std/json
F main() -> i64 {
json_str := ~{"name": "Alice", "age": 30}
obj := json_parse(json_str)
name := json_get_string(obj, "name")
age := json_get_number(obj, "age")
print_str(name) # "Alice"
print_i64(age) # 30
R 0
}
API 요약
파싱
| 함수 | 설명 |
|---|---|
json_parse(str) | JSON 문자열 파싱 → JsonValue |
json_get_type(val) | 타입 반환 (0=null, 1=bool, 2=number, 4=string, 5=array, 6=object) |
접근자
| 함수 | 설명 |
|---|---|
json_get_bool(val) | bool 값 추출 |
json_get_number(val) | i64 숫자 추출 |
json_get_string(val, key) | object에서 string 추출 |
json_get_object(val, key) | object에서 중첩 object 추출 |
json_get_array_item(val, idx) | array 인덱스 접근 |
json_array_len(val) | array 길이 |
생성
| 함수 | 설명 |
|---|---|
json_new_null() | null 값 생성 |
json_new_bool(val) | bool 값 생성 |
json_new_number(val) | number 값 생성 |
json_new_string(str) | string 값 생성 |
json_new_array() | 빈 array 생성 |
json_new_object() | 빈 object 생성 |
json_array_push(arr, val) | array에 추가 |
json_object_set(obj, key, val) | object에 키-값 추가 |
json_stringify(val) | JsonValue → JSON 문자열 |
실용 예제
예제 1: JSON 파싱 및 접근
U std/json
F main() -> i64 {
json_str := ~{
"user": {
"id": 123,
"name": "Bob",
"active": true
}
}
root := json_parse(json_str)
user := json_get_object(root, "user")
id := json_get_number(user, "id")
name := json_get_string(user, "name")
active := json_get_bool(user, "active")
print_i64(id) # 123
print_str(name) # "Bob"
print_i64(active) # 1
R 0
}
예제 2: JSON 배열 순회
U std/json
F main() -> i64 {
json_str := ~{"scores": [85, 92, 78, 90]}
root := json_parse(json_str)
scores := json_get_array(root, "scores")
len := json_array_len(scores)
i := 0
L i < len {
score := json_get_array_item(scores, i)
num := json_get_number(score)
print_i64(num)
i = i + 1
}
R 0
}
예제 3: 구조체 → JSON 직렬화
U std/json
S User {
id: i64,
name: i64,
age: i64
}
F user_to_json(user: User) -> i64 {
obj := json_new_object()
json_object_set(obj, "id", json_new_number(user.id))
json_object_set(obj, "name", json_new_string(user.name))
json_object_set(obj, "age", json_new_number(user.age))
R json_stringify(obj)
}
F main() -> i64 {
user := User { id: 456, name: "Charlie", age: 28 }
json_str := user_to_json(user)
print_str(json_str) # {"id":456,"name":"Charlie","age":28}
R 0
}
예제 4: JSON → 구조체 역직렬화
U std/json
S Config {
host: i64,
port: i64,
debug: i64
}
F parse_config(json_str: i64) -> Config {
obj := json_parse(json_str)
Config {
host: json_get_string(obj, "host"),
port: json_get_number(obj, "port"),
debug: json_get_bool(obj, "debug")
}
}
F main() -> i64 {
json_str := ~{"host": "127.0.0.1", "port": 8080, "debug": true}
config := parse_config(json_str)
print_str(config.host) # "127.0.0.1"
print_i64(config.port) # 8080
print_i64(config.debug) # 1
R 0
}
예제 5: 중첩 JSON 생성
U std/json
F main() -> i64 {
# {"users": [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]}
alice := json_new_object()
json_object_set(alice, "name", json_new_string("Alice"))
json_object_set(alice, "age", json_new_number(30))
bob := json_new_object()
json_object_set(bob, "name", json_new_string("Bob"))
json_object_set(bob, "age", json_new_number(25))
users := json_new_array()
json_array_push(users, alice)
json_array_push(users, bob)
root := json_new_object()
json_object_set(root, "users", users)
json_str := json_stringify(root)
print_str(json_str)
R 0
}
주의사항
1. 타입 안전
json_get_* 함수는 타입이 맞지 않으면 0 또는 null을 반환합니다. 사용 전 json_get_type()으로 확인하세요.
val := json_get_object(root, "key")
I json_get_type(val) == 6 { # 6 = object
# 안전하게 접근
nested := json_get_string(val, "field")
} E {
print_str("타입 불일치")
}
2. 메모리 관리
json_parse()는 동적 할당된 JsonValue를 반환합니다. 사용 후 json_free()를 호출하여 메모리를 해제하세요.
obj := json_parse(json_str)
D json_free(obj) # defer로 자동 정리
# obj 사용
3. 부동소수점 미지원
현재 구현은 정수(i64)만 지원합니다. 소수는 1,000,000을 곱한 스케일된 정수로 저장됩니다.
# 3.14 → 3140000 (3.14 * 1000000)
val := json_new_number_scaled(3140000)
json_str := json_stringify(val) # "3.14"
4. 대용량 JSON
재귀 파싱은 스택 오버플로 위험이 있습니다. 깊이 제한을 구현하거나, 이터레이티브 파서를 사용하세요.
# 최대 깊이 32
C MAX_JSON_DEPTH: i64 = 32
F safe_parse(json_str: i64, depth: i64) -> i64 {
I depth > MAX_JSON_DEPTH {
R 0 # 에러
}
# 파싱 로직...
}
5. 키 순서 미보장
JSON object는 해시맵 기반이므로, 키 순서가 보존되지 않습니다. 순서가 중요하면 array를 사용하세요.
# 키 순서 미보장
obj := json_parse(~{"a": 1, "b": 2, "c": 3})
# stringify 시 순서 변경 가능: {"c":3,"a":1,"b":2}
6. 이스케이프 문자
문자열 내 ", \, \n 등은 자동으로 이스케이프됩니다. 수동 이스케이프 불필요합니다.
str := ~{Hello\nWorld}
obj := json_new_string(str)
json_str := json_stringify(obj) # "Hello\\nWorld"
7. 에러 처리
json_parse()는 실패 시 null 또는 부분 파싱 결과를 반환합니다. 항상 json_get_type()으로 유효성을 확인하세요.
obj := json_parse(invalid_json)
I json_get_type(obj) == 0 { # 0 = null (파싱 실패)
print_str("JSON 파싱 에러")
R 1
}
8. UTF-8 지원
현재 구현은 ASCII만 완전 지원합니다. UTF-8 문자열은 바이트 단위로 처리되므로, 유니코드 이스케이프(\uXXXX)가 필요할 수 있습니다.
9. JSON Streaming
현재는 전체 문서를 메모리에 로드합니다. 대용량 스트림은 청크 단위 파싱을 구현하세요.
# 스트림 파싱 패턴
F parse_stream(file: File) -> Vec<i64> {
results := Vec::new()
L 1 {
chunk := file.read_str(1024)
I chunk == 0 { B }
obj := json_parse(chunk)
results.push(obj)
}
R results
}
10. 성능 최적화
- 반복 파싱 시
json_parse()결과를 캐싱하세요. - 대량 생성 시
json_stringify()를 한 번만 호출하세요. - 중첩 접근은 변수에 저장하여 반복 조회를 피하세요.
# 나쁜 예: 반복 조회
L i < 100 {
val := json_get_object(json_get_object(root, "data"), "field")
}
# 좋은 예: 한 번만 조회
data := json_get_object(root, "data")
L i < 100 {
val := json_get_object(data, "field")
}