본문으로 건너뛰기
jsonbeautifiers
한국어

JSONPath 테스터

JSONPath 표현식을 쓰면 결과가 실시간으로 보입니다. RFC 9535 문법.

문서
일치 결과

붙여 넣은 것은 여러분의 브라우저를 떠나지 않습니다. connect-src 허용 목록 덕분에 이는 약속이 아니라 브라우저가 강제하는 보장입니다. 직접 확인하기

JSONPath 표현식을 쓰면 내 문서에 대한 모든 일치 결과를 각각의 정규화된 경로와 함께 볼 수 있습니다. 원하는 것을 고를 때까지 고치고 다시 실행하세요.

여기 문법은 2024년 2월에 나온 IETF 제안 표준 RFC 9535를 따릅니다. 들리는 것보다 중요한 이야기인데, 그 전 17년 동안은 명세라는 것이 아예 없었기 때문입니다.

왜 방언을 밝혀야 하는가

JSONPath는 2007년 Stefan Goessner의 블로그 글에서 시작했습니다. 널리 구현되었지만 한 번도 명세화되지 않았고, 구현들은 흥미로운 거의 모든 지점에서 갈라졌습니다. $..가 루트를 포함하는지, 음수 인덱스가 무엇을 뜻하는지, $[0,1]이 합집합인지, 키가 없을 때 필터가 어떻게 동작하는지, 슬라이스 step이 0이면 어떻게 되는지. 어느 비교 프로젝트는 이런 불일치를 수백 건 정리했습니다.

RFC 9535가 정리했습니다. 어떤 방언을 구현했는지 말하지 않는 테스터는 질문 없이 답만 주는 셈이므로, 이 도구는 밝힙니다. RFC 9535이며, 아래에 적은 제외 항목이 있습니다.

여기서 지원하는 문법

$
문서의 루트. 모든 표현식이 여기서 시작합니다.
.name과 ['name']
이름이 있는 멤버. 공백이나 문장부호가 들어간 이름에는 대괄호 형태를 쓰세요.
.*와 [*]
객체의 모든 멤버, 또는 배열의 모든 요소.
..
자손 세그먼트. 이 단계와 그 아래 모든 단계를 훑습니다.
[0]과 [-1]
배열 인덱스. 음수는 끝에서부터 셉니다.
[1:5], [::2], [::-1]
슬라이스. RFC 9535의 의미를 따릅니다. 음수 step은 거꾸로 훑습니다.
[0, 2, 'name']
한 세그먼트에 여러 셀렉터. 결과의 합집합이 됩니다.
[?<표현식>]
필터. 안에서 @는 현재 요소, $는 문서의 루트입니다. == != < <= > >= 로 비교하고 && || 와 ! 로 조합합니다.
length() count() match() search() value()
RFC 9535가 정의한 함수 확장입니다. match()는 문자열 전체에 앵커하고 search()는 그러지 않습니다.

일부러 지원하지 않는 것

[(...)] 형태의 스크립트 표현식은 명세화된 적이 없고 RFC 9535가 제거했습니다. 부모를 가리키는 ^ 연산자는 일부 구현이 덧붙인 확장이고 RFC에는 없습니다. 그리고 의사 속성으로서의 @.length는 지금의 length(@)에 해당하는 RFC 이전 표기입니다. 이런 것을 붙여 넣으면, 조용히 빈 결과를 주는 대신 그렇다고 알려 줍니다.

JSONPath, JMESPath, jq, JSON Pointer

JSON 문서의 일부를 가리키는 네 가지 방법이고, 각각 쓰임이 다릅니다.

JSONPath
노드의 집합을 고릅니다. 깊이에 상관없이 패턴에 맞는 것을 전부 원할 때 가장 좋습니다. 이제 RFC 9535로 표준화되었습니다.
JMESPath
고르는 것에 더해 변환도 합니다. 프로젝션, 멀티셀렉트 해시, 파이프 표현식으로 출력의 모양을 바꿀 수 있습니다. AWS CLI가 씁니다. 처음부터 제대로 된 명세가 있었습니다.
jq
질의 문법이 붙은 온전한 언어입니다. 하려는 일이 선택보다 프로그래밍에 가까울 때 꺼내 쓰세요.
JSON Pointer(RFC 6901)
와일드카드도 필터도 없이 정확히 한 자리를 가리킵니다. 일부러 단순하게 만들었고, 그래서 JSON Patch와 JSON Schema가 둘 다 이걸 씁니다. 이스케이프는 둘뿐으로, 물결표가 ~0, 슬래시가 ~1입니다.

How to do this in code

같은 질의를 코드에서 실행하는 방법.

py Python
# jsonpath-ng is the most complete Python implementation
from jsonpath_ng.ext import parse

expr = parse('$.store.book[?(@.price < 10)].title')
titles = [m.value for m in expr.find(data)]

# JMESPath, if you prefer a specified language with projections
import jmespath
titles = jmespath.search('store.book[?price < `10`].title', data)
js JavaScript
import { JSONPath } from 'jsonpath-plus';

const titles = JSONPath({
  path: '$.store.book[?(@.price < 10)].title',
  json: data,
});

// Get the normalised paths rather than the values
const paths = JSONPath({ path: '$..author', json: data, resultType: 'path' });
sh jq

jq에는 자손과 필터를 한 번에 하는 연산자가 없어서 두 부분을 따로 씁니다.

# The jq equivalent of a filtered descendant search
jq '.store.book[] | select(.price < 10) | .title' data.json

# Every value at any depth under a key
jq '.. | .author? // empty' data.json
java Java

Jayway JsonPath는 RFC 9535보다 먼저 나왔고, 특히 없는 키에 대한 필터에서 명세와 다르게 동작합니다.

import com.jayway.jsonpath.JsonPath;

List<String> titles = JsonPath.read(json, "$.store.book[?(@.price < 10)].title");

자주 묻는 질문

표현식이 아무것도 돌려주지 않는 이유는 무엇인가요?
보통 셋 중 하나입니다. 공백이나 하이픈이 들어 있어 대괄호와 따옴표가 필요한 이름, 따옴표 없는 문자열과의 비교(@.type == book이 아니라 @.type == 'book'으로 쓰세요), 또는 문서가 객체인 자리에 배열을 가정한 경로. 표현식 자체가 잘못되었으면 위치와 함께 파싱 오류를 알려 주고, 표현식이 유효한데 맞는 게 없을 때만 결과가 비어 있습니다.
정규화된 경로가 무엇인가요?
RFC 9535는 일치한 위치를 적는 정식 표기를 정의합니다. 대괄호와 따옴표로 감싼 이름과 숫자 인덱스를 써서 $['store']['book'][0]['title']처럼 씁니다. 여기서 나오는 모든 일치 결과에 하나씩 붙기 때문에, 구현이 달라도 결과를 견줄 수 있습니다.
$..*는 $..와 같은 건가요?
아닙니다. 이것도 RFC가 정리한 불일치 중 하나입니다. $..*는 루트를 뺀 모든 자손 노드를 고릅니다. 반면 $.. 하나만으로는 완전한 표현식조차 되지 않습니다.