본문으로 건너뛰기
AZ Tools

JSON 스키마 검사기

왼쪽에 스키마를, 오른쪽에 문서를 붙여 넣으면 통과 여부와 함께 어디가 왜 틀렸는지를 한 줄씩 보여줍니다. 각 줄에는 문서 안의 JSON 포인터, 실패한 키워드, 그 키워드가 있는 스키마 위치, 문제가 된 값, 그리고 포인터만으로는 알 수 없는 붙여 넣은 문서의 줄 번호가 함께 나옵니다. 줄 순서는 문서를 읽는 순서와 같아서 맨 위 오류가 스크롤하며 처음 만나는 문제입니다. 이 도구의 핵심은 anyOf와 oneOf의 출력입니다. 대부분의 검사기는 분기별 오류를 한 덩어리로 쏟아내기 때문에 정작 필요한 문장이 묻힙니다. 여기서는 실패가 한 줄로 남고 그 아래에 분기별 요약과 오류가 붙으며, 문제 수가 가장 적은 분기를 "가장 근접"으로 표시합니다. 대개 그 분기가 작성자가 의도한 분기입니다. oneOf에서 두 분기가 동시에 통과한 경우도 별도의 실패로 보고하고 어떤 분기들인지 알려 줍니다. 드래프트도 답을 바꿉니다. 어떤 드래프트를 썼는지, 그리고 그것이 $schema에서 왔는지 선택값에서 왔는지 표시합니다. 드래프트 4에서 1.0은 정수가 아니고 exclusiveMinimum은 minimum을 수식하는 불리언이지만, 드래프트 6부터는 1.0이 정수이고 exclusiveMinimum이 경계값 자체입니다. 드래프트 7까지는 $ref 옆에 쓴 키워드가 모두 무시됩니다. 드래프트와 무관하게 헷갈리는 규칙도 있습니다. required는 존재만 따지므로 값이 null이어도 만족하고, minLength는 코드 포인트를 세므로 이모지는 1, 결합 문자로 쓴 악센트는 2이며, 0.3은 이진 부동소수점에서 0.1의 배수가 아니고, 키 순서만 다른 두 객체는 같은 값이지만 순서가 다른 배열은 다른 값입니다. 스키마 자체가 잘못된 경우는 다른 답입니다. 타입 이름 오타, 배열이 아닌 required, 컴파일되지 않는 pattern, 가리키는 곳이 없는 $ref는 문서의 유효/무효가 아니라 스키마 결함으로 위치와 함께 보고합니다. 알 수 없는 것도 분명히 해 둡니다. 네트워크를 쓰지 않으므로 URL을 가리키는 $ref는 해결할 수 없어 $defs로 가져와야 하고, unevaluatedProperties와 unevaluatedItems, $dynamicRef는 평가하지 않고 표시만 하며, 2^53을 넘는 정수는 배정밀도로 읽히고, 정규식은 사양이 요구하는 브라우저 엔진을 씁니다. 모든 처리는 페이지 안에서 이뤄지며 스키마도 문서도 밖으로 나가지 않습니다.

format은 기본적으로 주석일 뿐입니다. 실제로 실패시키려면 켜세요.

스키마에 $schema가 있으면 그 드래프트가 쓰이고, 없을 때만 이 설정이 쓰입니다.

결과

유효하지 않음

오류

7

드래프트

2020-12

문서

12줄, 169바이트

오류 7개2020-12 · $schema가 지정
문서 전체줄 1additionalProperties

스키마가 다른 속성을 허용하지 않는데 다음이 있습니다: "debug"

debug (줄 11)

값: {"name":"","version":"2.1","server":{"host":"0.0.0.0","port":99999,"tls":"yes"},"workers":…

스키마: /additionalProperties

/name줄 2minLength

문자열 길이가 코드 포인트 0개이고 최소는 1개입니다

값: ""

스키마: /properties/name/minLength

/version줄 3pattern

"2.1"는 패턴 ^\d+\.\d+\.\d+$과 맞지 않습니다

값: "2.1"

스키마: /properties/version/pattern

/server/port줄 6maximum

99999는 최댓값 65535보다 큽니다

값: 99999

스키마: /properties/server/properties/port/maximum

/server/tls줄 7type

"yes"는 string이고 스키마는 boolean를 요구합니다

값: "yes"

스키마: /properties/server/properties/tls/type

/workers줄 9anyOf

anyOf 분기 2개 중 어느 것도 이 값을 받아들이지 않습니다

값: 0

스키마: /properties/workers/anyOf

분기별 결과

분기 1 type: integer 문제 1개가장 근접

  • /workers — 0는 최솟값 1보다 작습니다

분기 2 const: "auto" 문제 1개

  • /workers — 0는 상수 "auto"와 다릅니다
/tags줄 10uniqueItems

0번 인덱스와 1번 인덱스의 항목이 같은 값입니다: "a"

값: ["a","a"]

스키마: /properties/tags/uniqueItems

모든 처리는 이 페이지 안에서 이뤄지며 스키마도 문서도 업로드되지 않습니다.

사용법

  1. 첫 번째 상자에 스키마를, 두 번째 상자에 검사할 문서를 붙여 넣습니다. 입력은 저장되므로 페이지를 닫았다 열어도 남습니다.
  2. 요약 타일에서 통과 여부, 오류 개수, 사용한 드래프트, 문서 크기를 확인합니다. 드래프트가 $schema에서 왔는지 선택값인지도 함께 표시됩니다.
  3. 오류 목록을 위에서부터 봅니다. 각 줄에 JSON 포인터, 문서의 줄 번호, 실패한 키워드, 그 자리에 있던 값, 스키마 안의 위치가 나오므로 어느 쪽을 고칠지 바로 판단할 수 있습니다.
  4. anyOf나 oneOf 줄에서는 분기 목록을 읽습니다. "가장 근접"으로 표시된 분기가 가장 적게 실패한 분기이며 대개 의도한 분기입니다.
  5. format을 실제로 실패시키고 싶으면 "format 검사"를 켭니다. 사양상 format은 주석이므로 기본값은 꺼짐입니다.

자주 묻는 질문

틀릴 줄 알았는데 왜 통과하나요?
대개 스키마가 보기보다 적게 말하고 있기 때문입니다. JSON 스키마는 모르는 키워드를 무시하므로 required 대신 require, minLength 대신 minlength처럼 오타가 나거나 그 드래프트에 없던 키워드를 쓰면 오류가 아니라 아무 일도 하지 않는 주석이 됩니다. 객체는 additionalProperties가 막지 않는 한 추가 속성을 받아들이고, 배열은 items나 prefixItems가 없으면 무엇이든 받으며, format은 검사를 켜기 전까지 주석일 뿐입니다. 타일에 표시된 드래프트도 확인하세요. 선택한 드래프트에 없는 키워드는 이 도구도 무시합니다.
1.0은 정수인가요?
드래프트 6 이상에서는 그렇습니다. 사양이 정수를 값으로 정의하므로 1.0과 1.0e2는 정수이고 1.5는 아닙니다. 드래프트 4에서는 소수점을 찍어 쓴 수는 실수이므로 "type": "integer"에서 실패합니다. 이 도구는 파싱할 때 숫자 리터럴의 표기 방식을 함께 기억하기 때문에 1과 1.0을 구분할 수 있습니다. JSON.parse만 쓰면 둘은 같은 값이 되어 버립니다. 오래된 라이브러리와 draft-04 스키마를 쓰고 있다면 이 차이는 실제 장애로 이어지며, 드래프트 선택을 바꿔 그대로 재현할 수 있습니다.
0.3은 왜 0.1의 배수가 아닌가요?
두 값 모두 이진 부동소수점에서 정확하지 않기 때문입니다. 0.3을 0.1로 나누면 3이 아니라 2.9999999999999996이고, 참조 구현들이 바로 이 몫을 검사하므로 문서는 거부됩니다. 값을 3으로 바꾸고 multipleOf를 1로 쓰면 통과합니다. 이 도구만의 특징이 아니라 python-jsonschema, ajv 등 대부분이 소수 multipleOf에서 같게 동작합니다. 그래서 "소수점 두 자리"를 뜻하려면 pattern을 쓰거나 센트 단위 정수로 표현하는 편이 낫습니다.
오류의 JSON 포인터는 어떻게 읽나요?
포인터는 문서 루트에서 시작해 슬래시로 이어지는 경로입니다. /server/port는 server 객체의 port이고 /tags/2는 tags의 세 번째 항목입니다. 인덱스는 0부터 셉니다. 빈 포인터는 문서 전체를 뜻하며, required나 additionalProperties, uniqueItems의 오류가 멤버가 아니라 그것을 담은 객체나 배열을 가리키는 이유이기도 합니다. 없는 속성에는 자기 자리가 없기 때문입니다. 속성 이름 안의 /는 ~1로, ~는 ~0으로 씁니다. 포인터 옆의 줄 번호는 붙여 넣은 원문에서 그 경로가 실제로 놓인 줄입니다.
다른 검사기는 왜 anyOf에서 오류가 쏟아지나요?
anyOf가 실패했다는 것은 모든 분기가 실패했다는 뜻이고 그 오류들은 전부 사실이기 때문입니다. 구조를 두지 않는 검사기는 이를 나란히 출력하므로, 문자열과 객체의 합집합에서 "문자열이 아니다"라는 문장 옆에 의도하지도 않은 객체의 필수 속성 오류가 붙습니다. 분기별로 묶으면 스키마가 원래 가지고 있던 구조가 되살아나고, 실패가 가장 적은 분기를 고르면 대개 의도한 분기에 닿습니다. 다만 이는 증명이 아니라 추정이며, 두 분기가 각각 하나씩 실패하면 앞의 것을 표시할 뿐 스키마만으로는 무엇을 의도했는지 알 수 없습니다.
다른 파일이나 URL을 가리키는 $ref도 따라가나요?
아니요, 의도적으로 그렇습니다. 네트워크를 쓰지 않으므로 https://example.com/user.json이나 common.json#/$defs/id 같은 참조는 해결할 수 없으며, 조용히 넘어가지 않고 스키마 결함으로 보고합니다. 붙여 넣은 문서 안의 참조는 모두 동작합니다. #/$defs/name, 단순한 #, 루트로 돌아오는 재귀 참조, $anchor, 다른 참조가 이름으로 부르는 $id까지 지원합니다. 파일이 나뉜 스키마를 검사하려면 먼저 하나로 묶으세요. 참조 대상을 $defs에 넣고 경로를 바꾸는 방식이며, 배포 전에 대부분의 도구가 하는 일이기도 합니다.

관련 도구