jxn 읽는 법 -- 0.3
jxn(JSON eXplained Notation)은 JSON 의 형태와 뜻을 정의하는 언어(스키마 언어)다. .jxn 파일에 사람이 읽는 정의를 쓰고, 뜻과 관계를 적고, JSON Schema 로 옮긴다.
이 문서는 .jxn 파일과 jxn doc 이 낸 페이지를 읽는 사람을 위한 것이다 -- 낯선 표기를 표기 사전에서 찾는다. 정확한 규칙은 명세에 있다.
한 줄로:
{ }는 object,[ ]는 배열,|줄은 갈래와 값 목록, 따옴표는 인스턴스에 나오는 키와 값, 맨 낱말은 타입,//는 뜻,@는 주석, 관계 낱말로 시작하는 줄은 관계.
한 파일 따라 읽기#
주문 하나를 정의한 작은 파일이다. 예는 합성이다.
jxn 0.3
anchor change = $.history : Change // change[-1] = 바로 앞 Change
anchor catalog external // 가게의 상품 목록 -- 인스턴스 밖
{ // 주문 하나
"id": string, // 주문 번호
"items": [{ // 담은 상품
"sku": string, // 상품 코드
refers-to catalog.items
"price": number // 담을 때의 값
}] min 1,
"total": number, // 합계 금액
derived-from items[*].price // 담은 상품 값의 합
"note"?: string, // 주문 메모 -- 없을 수 있다
"coupon": null | string, // 쿠폰 번호 -- 늘 있고, 없으면 null
"method": "card" | "cash", // 결제 방법
"payment"?: by method // 결제 내용 -- 방법에 따라
| "card" => { "last4": string } // 카드 끝 네 자리
| "cash" => absent // 현금이면 없다
"history": [ Change | Note ] // 주문 이력
}
def Change { // 단계가 바뀐 기록
"from": State, // 바뀌기 전 단계
equals change[-1].to // 바로 앞 변경의 도착 단계에서 출발한다
"to": State // 바뀐 단계
}
def Note { // 사람이 남긴 메모
"text": string // 메모 글
}
def State = // 주문의 단계
| "open" // 담는 중
| "paid" // 결제됨
| "shipped" // 보냄이 정의에 맞는 인스턴스:
{
"id": "A-1",
"items": [{ "sku": "P-10", "price": 1200 }, { "sku": "P-11", "price": 300 }],
"total": 1500,
"coupon": null,
"method": "card",
"payment": { "last4": "4242" },
"history": [{ "from": "open", "to": "paid" }, { "text": "포장 부탁" }]
}줄마다 읽으면:
jxn 0.3-- 이 파일이 쓰는 문법의 판. (0.3 은 0.2 에 글 속의 링크와 키 참조를 더했다 --jxn 0.2파일도 그대로 읽힌다)anchor change = $.history : Change-- 앵커.$는 인스턴스의 뿌리,$.history는 뿌리의history배열이다.: Change는 그 배열에서Change갈래만 센다는 뜻이다. 그래서change[-1]은 "바로 앞의 Change" 다 (메모Note는 건너뛴다). ->$· 앵커 ·: Def·[-1]anchor catalog external-- 인스턴스 밖의 것(가게의 상품 목록)을 가리키는 이름. -> external{-- 이름 없는{가 뿌리다. 인스턴스가{로 시작하듯 정의도{로 시작한다. ->{ }"id": string,-- 키. 따옴표 안이 인스턴스의 키, 뒤의 맨 낱말이 값의 타입, 줄 끝//가 뜻이다. 쉼표는 써도 되고 빠져도 된다. -> 키 ·//"items": [{ ... }] min 1,-- 배열. 원소마다{ "sku", "price" }인 object 이고, 적어도 하나다. ->[ ]·minrefers-to catalog.items-- 관계. 바로 위의 키sku가 주어다: "sku 의 값은 카탈로그의 상품을 가리킨다". -> 관계 · 관계 낱말derived-from items[*].price--total은 모든 원소의price에서 계산된다.[*]는 배열의 모든 원소다. ->[*]"note"?: string--?는 키가 없을 수 있다."coupon": null | string은 키는 늘 있고 값이 null 일 수 있다. 둘은 다르다. ->?·null |"method": "card" | "cash"-- 허용 값. 따옴표 글은 인스턴스에 실제로 나오는 값이다. -> 허용 값"payment"?: by method와 아래|줄 -- 갈래.method가"card"이면payment는{ "last4": string },"cash"이면payment키가 없다(absent). ->by·absent"history": [ Change | Note ]-- 원소가Change이거나Note인 배열. 어느 쪽인지는 어느 키가 있나로 갈린다. ->defequals change[-1].to--from은 바로 앞 Change 의to와 같다. 이웃 원소를 가리키는 주소는 jxn 만의 것이다. ->[-1]def State =와 아래|줄 -- 값마다 뜻을 단 값 목록. 한 줄로 쓰면"open" | "paid" | "shipped"와 같다. -> 값 목록
표기 사전#
| 표기 | 뜻 | 항목 |
|---|---|---|
{ } |
닫힌 object -- 적힌 키만 | object |
[ T ] |
배열 | 배열 |
"key": T |
키와 그 값의 타입 | 키 |
? |
키가 없을 수 있다 | 없을 수 있다 |
null | |
값이 null 일 수 있다 | null |
"a" | "b" |
허용 값 | 허용 값 |
| "a" // 뜻 |
값마다 뜻을 단 값 목록 | 값 목록 |
by k · => |
이웃 키에 따라 모양이 다르다 | 갈래 |
absent |
그 갈래에서는 키가 없다 | absent |
{ [k]: T } |
키가 열린 object (map) | map |
...Name |
다른 모양의 키를 펼친다 | 펼치기 |
expect T |
기대하는 모양 (원문) | expect |
any · unknown |
무엇이든 · 아직 모른다 | any · unknown |
min · max |
범위 | 범위 |
def Name |
이름 붙은 모양 | def |
// |
뜻 · 풀이 | 뜻과 풀이 |
// ? |
열린 물음 | 물음 |
// # |
머리 -- 무리 가르기 | 머리 |
@name 값 |
주석 | 주석 |
@status |
정의의 상태 | 상태 |
[글](주소) · https://... |
바깥 문서로 가는 링크 | 링크 |
글 속의 in.body · event |
문서 안의 키 참조 (저절로) | 키 참조 |
equals · derived-from ... |
관계 | 관계 · 낱말 |
when |
관계의 조건 | when |
.sub |
주어 좁히기 | 주어 좁히기 |
$ |
뿌리 | 뿌리 |
. |
아래 키 | 아래 키 |
[*] |
배열의 모든 원소 | 모든 원소 |
.* |
map 의 모든 값 | 모든 값 |
[-1] · [+1] |
이웃 원소 | 이웃 |
[..-n] |
앞의 모든 원소 | 앞의 모든 원소 |
anchor |
주소가 시작하는 이름 | 앵커 |
: Def |
그 갈래만 센다 | 갈래만 |
external |
인스턴스 밖 | external |
home |
앵커 없는 주소의 시작 | home |
뼈대 -- 모양#
{ } object#
적힌 키만 있는 object. 적히지 않은 키가 인스턴스에 있으면 틀린 것이다. 이름 없는 { 는 파일의 뿌리(인스턴스 전체)다. 한 줄로 { "last4": string } 처럼도 쓴다. 명세 2-3 · 2-5
[ T ] 배열#
원소마다 T 인 배열. [ Item ] · [ string ] · [ Change | Note ]. 원소가 여러 줄 object 이면 [{ 로 열고 }] 로 닫는다.
"items": [{ // 담은 상품
"sku": string
}] min 1"key": T 키#
인스턴스의 키(따옴표 안)와 그 값의 타입. 키는 늘 따옴표로 쓴다 -- JSON 과 같다. 줄 끝의 // 가 그 키의 뜻이다. 명세 2-4
? 없을 수 있다#
"note"?: string -- 키가 없을 수 있다. 있으면 글이다 (null 은 안 된다). TypeScript 의 ?: 와 같다.
{ } // 된다
{ "note": "문 앞" } // 된다
{ "note": null } // 안 된다 -- null 은 null | string 으로 적는다null | null 일 수 있다#
"coupon": null | string -- 키는 늘 있고, 값이 null 일 수 있다. "칸은 늘 있고, 할 일이 없으면 null" 인 기록에서 ? 와 다른 뜻이다. 둘 다면 "coupon"?: null | string. 명세 2-4
"a" | "b" 허용 값#
따옴표 글 · 수 · true · false 는 인스턴스에 실제로 나오는 값이다. | 는 "또는". "method": "card" | "cash" 이면 method 는 둘 가운데 하나다. 맨 낱말(string · Item)은 값의 종류(타입)다. 명세 2-5
| 줄 -- 값 목록#
값마다 뜻을 달고 싶을 때, 타입 자리를 비우고 다음 줄부터 | 값 을 쓴다.
"state": // 주문의 단계
| "open" // 담는 중
| "paid" // 결제됨한 줄로 쓴 "state": "open" | "paid" 와 같은 뜻이다. // # 머리로 값을 무리로 가르기도 한다. 명세 2-6
by 갈래#
"payment"?: by method -- payment 의 모양은 이웃 키 method 의 값에 따라 다르다. 아래 | 줄이 갈래다: | "card" => 모양 은 "method 가 "card" 이면 이 모양". 갈래는 method 의 값을 빠짐없이 덮는다.
"payment"?: by method
| "card" => { "last4": string }
| "cash" => absentobject 안에서 by kind 만 한 줄로 쓰면, kind 의 값에 따라 그 object 의 키 집합이 달라진다. 갈래 안에 적은 관계에는 "그 갈래일 때" 라는 조건이 저절로 붙는다. 명세 2-8
absent#
그 갈래에서는 키가 없다. | "cash" => absent 이면 method 가 "cash" 인 인스턴스에는 payment 키가 없어야 한다. 그래서 그 키는 ? 가 붙는다. 명세 2-8
{ [k]: T } map#
키가 정해지지 않은 object. "stock": { [sku]: integer } 는 "어떤 키든 -- 그 키는 상품 코드(sku) -- 값은 integer".
{ "stock": { "P-10": 3, "P-11": 0 } }...Name 펼치기#
...Address -- Address 의 키를 이 object 에 그대로 펼친다. 그 위에 키를 더할 수 있다. 명세 2-7
expect T 기대하는 모양#
밖에서 온 원문을 그대로 남기는 칸. 기대하는 모양은 T 지만, 어긋난 원문도 올바른 기록이다 -- 표준 검사는 통과시키고, jxn check 가 어긋남을 알린다. 명세 2-10
any · unknown#
any 는 일부러 연 칸(무엇이든). unknown 은 타입을 아직 모른다 -- jxn doc 페이지에는 "?" 로 보인다. 명세 2-5
min · max 범위#
수에 붙으면 크기(integer min 1), 배열 · 글 · map 에 붙으면 길이([ Item ] min 1 = 적어도 하나). 명세 2-5
def 이름 붙은 모양#
def Item { ... } -- 여러 자리가 같이 쓰는 모양에 이름을 붙인다. 이름은 대문자로 시작한다 (string 같은 기본 타입과 갈린다). def State = "open" | "paid" 처럼 타입식에도 이름을 붙인다. [ Change | Note ] 처럼 object def 둘의 합이면 어느 키가 있나로 갈린다. 기본 타입은 string · integer · number · boolean. 명세 2-3
뜻과 주석#
// 뜻과 풀이#
줄 끝의 // 글 은 그 줄의 뜻(한 문장)이다. 줄 전체가 // 글 이면 바로 위 선언의 풀이(더 할 말)다. 뜻 안의 -- 는 그냥 글이다. 명세 1
// ? 물음#
아직 정하지 못한 것. jxn doc 페이지의 점검 칸에 모인다. 명세 1
// # 머리#
아래 형제들(키 · 값 · 갈래)을 한 무리로 묶는다. 맨 위 수준이면 파일의 절이다. 명세 1
@name 값 주석#
@owner customer @status stable -- 파일이 스스로 선언한 칸(누가 채우나 · 누가 읽나 · 출처 ...). 이름과 값의 꼴은 파일 위쪽의 tag 줄과 list 줄에 있다.
list owner // 누가 채우나 -- 파일이 선언한 어휘
| customer // 고객이 넣은 값
| system // 시스템이 계산한 값
tag owner : set<owner> inherit needinherit 인 주석은 조상에서 이어받는다 -- jxn doc 의 주석 표에 이어받음 으로 보인다. need 이면 모든 키에 있어야 한다. 이어받기의 순서는 명세 2-14. 명세 2-1 · 2-2
@status 상태#
정의 자체의 상태. jxn 이 준다: stable 정함 · draft 초안 · stub 모름(이름만 있다). jxn doc 의 배지와 점검 칸이 이것을 본다. 명세 2-15
[글](주소) 링크#
뜻 · 풀이 · 물음 · 값의 뜻 · @src 같은 글 주석 값 안의 [글](주소) 와 맨 https://... 주소는 링크다. jxn doc 페이지에서 누르면 그리로 간다.
"total": number, // 합계 금액 -- [계산 규칙](docs/계산.md)
@src [주문 규격 4절](docs/주문.md), https://example.com/order-spec상대 주소(docs/계산.md)는 그 .jxn 파일이 있는 폴더에서 찾는다. 파일은 열리기만 한다 -- 그 파일의 몇째 줄까지 가지는 않는다. 명세 2-16
키 참조 -- 저절로#
글 안에 적힌 키 이름 · 주소(in.body · items[*].price · event)가 그 자리에서 정의의 키로 풀리면, jxn doc 페이지에서 누를 수 있다 -- 누르면 그 키의 설명이 열린다. 쓰는 사람이 따로 표시하지 않는다. 풀리지 않는 말은 그냥 글이다.
"total": number, // 합계 금액
// items[*].price 를 더하고, 할인(discount)이 정액이면 discount.amount 를 뺀다위에서 items[*].price · discount · discount.amount 가 누를 수 있다. 낱말 하나는 가까운 키(아래 키 · 형제 키 · home)에서 찾는다. 명세 2-16
관계#
관계 줄#
키 아래에 들여 쓴 낱말 주소 줄. 바로 위의 키가 주어다.
"total": number, // 합계 금액
derived-from items[*].price // 담은 상품 값의 합"total 은 items 의 모든 price 에서 계산된다". 관계는 주어 쪽에 한 번만 적는다 -- jxn doc 페이지는 과녁(price) 쪽에도 같은 낱말로 보여 준다 (거꾸로 절의 derived-from total → 이 키). 관계 낱말은 페이지에서도 파일에 적힌 그대로이고, 아래 표의 뜻과 거꾸로 읽기는 낱말에 마우스를 올리면 뜬다. 명세 2-11
관계 낱말#
when 조건#
derived-from discount.amount when discount.kind == "fixed" -- 그 조건일 때만 서는 관계. == · != · in [...] 과 and. 갈래 안의 관계에는 갈래의 조건이 저절로 붙어, jxn doc 은 합친 조건을 보인다. 명세 2-13
.sub 주어 좁히기#
.code refers-to catalog.vouchers -- 주어를 이 키 아래의 code 로 좁힌다. 여러 자리가 같이 쓰는 모양(def)을 쓰는 자리에서, 그 안쪽 키의 관계를 적을 때 쓴다. 명세 2-11
주소#
관계 · when · anchor 가 가리키는 자리. 뿌리 $ · 모든 원소 [*] · 모든 값 .* 는 JSONPath 와 같다. 명세 2-12
$ 뿌리#
인스턴스 JSON 전체. $.history 는 뿌리의 history 키, $.orders[*] 는 뿌리의 orders 배열의 모든 원소다. jxn doc 페이지의 키 경로도 $ 에서 시작한다 ($.orders[*].total).
. 아래 키#
a.b -- a 안의 키 b. discount.kind 는 discount 안의 kind.
[*] 모든 원소#
배열의 모든 원소. items[*].price 는 "items 의 모든 원소의 price". 앵커에 붙으면 "아무 원소" (order[*].id = 어느 주문이든의 id).
.* 모든 값#
map 의 모든 값. stock.* 는 stock 의 모든 값(재고 수).
[-1] · [+1] 이웃 원소#
앵커 배열에서 지금 원소의 바로 앞([-1]) · 뒤([+1]) 원소. change[-1].to 는 "바로 앞 Change 의 to". JSONPath 의 [-1](마지막 원소)과 다르다 -- jxn 만의 표기다. [-2] 는 두 번째 앞.
[..-n] 앞의 모든 원소#
지금 원소보다 n 번째 앞까지의 모든 원소. change[..-1] 은 지금보다 앞의 모든 Change.
anchor 앵커#
anchor change = $.history : Change -- 주소가 시작하는 이름을 짓는다. 배열에 이름을 지으면 그 이름에 [-1] · [+1] · [*] · [..-n] 을 붙인다. 주소의 첫 이름이 앵커 이름이면 앵커다.
: Def 갈래만 세기#
anchor change = $.history : Change -- history 의 원소 가운데 Change 만 센다. 그래서 change[-1] 은 사이에 낀 다른 갈래(Note)를 건너뛴 바로 앞 Change 다.
external 인스턴스 밖#
anchor catalog external -- 인스턴스에 없는 것(참조 데이터 · 계산으로만 있는 상태). 그리로 가는 관계는 그것을 함께 받아야 검사된다. jxn doc 페이지에는 "밖" 으로 보인다.
home#
home $.history[*] -- 앵커 없이 쓴 주소(in.from 같은)가 시작하는 자리. 없으면 뿌리다. 이력의 한 원소 안에서 이웃 키를 가리킬 때 쓴다.
jxn doc 페이지 읽기#
jxn doc FILE.jxn --example EX.json 이 낸 html 한 파일. 인터넷 없이 열린다.
- 왼쪽 나무 -- JSON 처럼 그린다. 뿌리
{에서 시작해}로 닫고, 줄은"key": "type",. 안이 있는 키는 열면"items": [{로 시작해 자식들 뒤 제 줄의}],로 닫고, 접으면"items": [{ … }],한 줄이다. 흐린[method = card]는 키가 아니라 갈래다 (by뒤에는 .jxn 처럼 쉼표가 없다). 깊이마다 세로 줄이 그어지고, 고른 키가 든 블록의 줄은 강조색이다. 위아래 화살표로 옮기고, 엔터 · 좌우 화살표로 열고 접는다. 나무와 설명 사이의 세로 선은 끌어서 옮긴다 -- 너비는 페이지마다 남고, 선을 두 번 누르면 처음 너비로 돌아간다. 배지: draft · stub · 상태 없음(status 주석 -- 값을 적힌 그대로 보인다) · 열(배열) · 맵 · 관계 n · 되풀이(같은 모양이 위에 있다),--badge로 고른 주석은 값이 바뀌는 자리에만. - 오른쪽 설명 -- 경로(
$에서 시작), 뜻, 배지(상태, 그리고--head-badge로 고른 주석은 값마다 하나 -- 이것들은 주석 표에 다시 나오지 않는다), 표기(이 키에 쓰인 표기와 이 문서의 항목), 풀이(이름표 · 상자 없이 뜻 아래에 이어지는 글) · 물음, 주석 표(값마다 적음 · def 머리 · 이어받음), 허용 값, 관계(낱말은 파일에 적힌 그대로, 누르면 과녁으로)와 거꾸로(이 키를 가리키는 관계 --derived-from total → 이 키처럼 같은 낱말로), 모양(def 와 그 모양을 쓰는 자리), 안에 있는 것, 예시 값과 나온 자리. - 점검 칸 -- status 가 없거나 draft · stub 인 키, 열린 물음, 예시에만 있는 키, 예시에 값이 없는 키.
- 주소 링크 -- 주소창의
#<키 id>로 키 하나를 가리켜 공유한다. 머리의 제목을 누르면 뿌리. - 표기 위에 마우스를 올리면 뜻 한 줄이 뜨고, 누르면 이 문서의 그 항목으로 온다.
- 뜻 · 풀이 속의 키 이름(밑줄 친 글)을 누르면 그 키의 설명이 열린다 -- 뒤로 가기로 돌아온다. 링크(
[글](주소)·https://...)는 보통 링크처럼 그 문서로 간다.