| RFC 9535 | JSONPath | 2024년 2월 |
| Gössner 등 | 표준 트랙 | [페이지] |
JSONPath는 주어진 JSON 값 내에서 JSON(RFC 8259) 값을 선택하고 추출하기 위한 문자열 구문을 정의한다.¶
이것은 인터넷 표준 트랙 문서이다.¶
이 문서는 Internet Engineering Task Force (IETF)의 산출물이다. 이는 IETF 커뮤니티의 합의를 나타낸다. 이 문서는 공개 검토를 받았으며 Internet Engineering Steering Group (IESG)에 의해 게시가 승인되었다. 인터넷 표준에 대한 추가 정보는 RFC 7841의 섹션 2에서 확인할 수 있다.¶
이 문서의 현재 상태, 정오표 및 이에 대한 피드백 제공 방법에 관한 정보는 https://www.rfc-editor.org/info/rfc9535에서 얻을 수 있다.¶
Copyright (c) 2024 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
JSON [RFC8259]은 구조화된 데이터 값을 위한 널리 쓰이는 표현 형식이다. JSONPath는 주어진 JSON 값 내에서 JSON 값을 선택하고 추출하기 위한 문자열 구문을 정의한다.¶
JSON Pointer [RFC6901]와 관련하여, JSONPath는 이를 대체하기 위한 것이 아니라 더 강력한 동반자로 의도되었다. 부록 C를 참조하라.¶
이 문서에서 핵심어 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", 및 "OPTIONAL"은 여기에 표시된 것처럼 모두 대문자로 나타날 때, 그리고 그때에만 BCP 14 [RFC2119] [RFC8174]에 설명된 대로 해석되어야 한다.¶
이 문서의 문법 규칙은
[RFC5234]에 설명된 대로 ABNF로 해석되어야 한다.
이 문서의 ABNF 터미널 값은 UTF-8 인코딩이 아니라
유니코드 스칼라 값을 정의한다.
예를 들어, 유니코드 PLACE OF INTEREST SIGN (U+2318)은
ABNF에서 %x2318로 정의될 것이다.¶
함수는 fname()에서처럼 함수 이름 뒤에 한 쌍의
괄호를 붙여 지칭한다.¶
아래에서 명확히 하는 경우를 제외하고 [RFC8259]의 용어가 적용된다. "primitive"와 "structured"라는 용어는 [RFC8259]의 섹션 1에서와 같이 서로 다른 종류의 값을 묶는 데 사용된다. JSON 객체와 배열은 structured이고, 그 밖의 모든 값은 primitive이다. "object", "array", "number", "string"에 대한 정의는 변경되지 않는다. 특히 "object"와 "array"는 일반적인 프로그래밍 맥락에서처럼 일반적인 의미를 갖지 않는다.¶
이 문서에서 사용되는 추가 용어는 아래에 정의되어 있다.¶
[RFC8259]에 따르면, JSON의 일반 데이터 모델에 부합하는 데이터 항목, 즉 primitive 데이터(숫자, 텍스트 문자열, 그리고 특수 값 null, true, false) 또는 structured 데이터(JSON 객체와 배열)이다. [RFC8259]는 JSON 값의 텍스트 표현에 초점을 맞추며 여기서 가정하는 값 추상화를 완전히 정의하지는 않는다.¶
객체 안의 이름/값 쌍이다. (멤버 자체는 값이 아니다.)¶
멤버를 구성하는 이름/값 쌍에서의 이름(문자열)이다. 이는 [RFC8259]에서도 사용되지만, 해당 명세는 이를 형식적으로 정의하지 않는다. 완전성을 위해 여기에 포함하였다.¶
JSON 배열 안의 값이다.¶
배열 안의 특정 요소를 식별하는 정수이다.¶
JSONPath 표현식의 짧은 이름이다.¶
JSONPath 표현식이 적용되는 값의 짧은 이름이다.¶
쿼리 인자 내에서 값의 위치이다. 이는 쿼리 인자 안의 객체와 배열을 통해 값으로 이동하는 이름과 인덱스의 시퀀스로 생각할 수 있으며, 빈 시퀀스는 쿼리 인자 자체를 나타낸다. 위치는 Normalized Path(아래에 정의됨)로 표현될 수 있다.¶
값과 쿼리 인자 내에서의 해당 위치가 함께 이루는 쌍이다.¶
그 값이 전체 쿼리 인자인 유일한 노드이다.¶
쿼리 인자의 루트 노드를 참조하는
표현식 $이다.¶
필터 표현식 평가의 맥락에서 현재 노드를 참조하는
표현식 @이다(나중에 설명됨).¶
노드가 배열이면 그 요소들의 노드이고, 노드가 객체이면 그 멤버 값들의 노드이다. 노드가 배열도 객체도 아니면 자식을 갖지 않는다.¶
노드의 자식과 그 자식들의 자식, 그리고 그 이후를 재귀적으로 포함한다. 더 형식적으로는, 노드들 사이의 "하위 노드" 관계는 "자식" 관계의 추이적 폐쇄이다.¶
값 내에서 해당 노드의 조상 수이다. 값의 루트 노드는 깊이가 0이고, 루트 노드의 자식은 깊이가 1이며, 그 자식들은 깊이가 2이고, 이런 식으로 계속된다.¶
노드들의 리스트이다. 노드 리스트는 예를 들어 배열처럼 JSON으로 표현될 수 있지만, 이 문서는 특정 표현을 요구하거나 가정하지 않는다.¶
함수 표현식에서 함수 인자 (실제 파라미터)를 받을 수 있는 (함수의) 형식 파라미터이다.¶
값 안의 노드를 식별하는 JSONPath 표현식의 한 형태로, 정확히 그 노드만을 결과로 산출하는 쿼리를 제공한다. 쿼리 인자 안의 각 노드는 정확히 하나의 Normalized Path로 식별된다 (우리는 그 Normalized Path가 해당 노드에 대해 "고유"하다고 말한다). 특정 쿼리 인자에 대한 Normalized Path가 되려면, Normalized Path는 정확히 하나의 노드를 식별해야 한다. 이는 JSON Pointer [RFC6901]와 유사하지만, 구문적으로는 다르다. 참고: 이 정의는 섹션 2.7의 구문적 정의를 기반으로 한다. 값 안의 노드를 식별하지만 해당 구문에 부합하지 않는 JSONPath 표현식은 Normalized Path가 아니다.¶
high-surrogate 및 low-surrogate 코드 포인트를 제외한 모든 유니코드 [UNICODE] 코드 포인트(다시 말해, 포함적인 16진수 범위 0부터 D7FF 또는 E000부터 10FFFF까지의 정수)이다. JSONPath 쿼리는 유니코드 스칼라 값의 시퀀스이다.¶
입력 값의 자식
([<selectors>])
또는 하위 노드(..[<selectors>])를 선택하는 구성요소 중 하나이다.¶
세그먼트 내의 단일 항목으로, 입력 값을 받아 입력 값의 자식 노드로 이루어진 노드 리스트를 생성한다.¶
특정 방식으로 구문적으로 제한된 세그먼트(섹션 2.3.5.1)로 구성된 JSONPath 표현식으로, 입력 값과 관계없이 표현식이 최대 하나의 노드만 포함하는 노드 리스트를 산출한다. 참고: 항상 단일 노드 리스트를 산출하지만 섹션 2.3.5.1의 구문에 부합하지 않는 JSONPath 표현식은 단일 쿼리가 아니다.¶
이 문서는 쿼리 인자를 JSON 값들의 트리로 모델링하며, 각 값은 자체 노드를 가진다. 노드는 루트 노드이거나 그 하위 노드 중 하나이다.¶
이 문서는 쿼리를 쿼리 인자에 적용한 결과를 노드 리스트(노드들의 리스트)로 모델링한다.¶
노드는 쿼리 인자에서 선택 가능한 부분이다. 객체에서 쿼리로 선택할 수 있는 유일한 부분은 멤버 값이다. 멤버 이름과 멤버(이름/값 쌍)는 선택할 수 없다. 따라서 멤버 값은 노드를 가지지만, 멤버와 멤버 이름은 노드를 갖지 않는다. 마찬가지로 멤버 값은 객체의 자식이지만, 멤버와 멤버 이름은 그렇지 않다.¶
이 문서는 Stefan Gössner의 인기 있는 JSONPath 제안(2007-02-21일자) [JSONPath-orig]을 기반으로 하며, 그 구현이 널리 배포되면서 얻은 경험을 바탕으로 이에 대한 규범적 명세를 제공한다.¶
부록 B는 JSONPath가 XML의 XPath [XPath]에서 어떻게 영감을 받았는지 설명한다.¶
JSONPath는 PHP 및 JavaScript와 같은 프로그래밍 언어의
JSON 구현을 위한 가벼운 동반자로 의도되었으므로,
XPath가 그랬던 것처럼 자체 표현식 언어를 정의하는 대신,
JSONPath는 쿼리의 일부를 하위
런타임, 예를 들어 JavaScript의 eval() 함수에 위임했다.
JSONPath가 더 많은 환경에서 구현됨에 따라, JSONPath
표현식의 이식성은 점점 낮아졌다.
예를 들어 정규 표현식 처리는 종종
편리한 정규 표현식 엔진에 위임되었다.¶
이 문서는 이러한 구현별 의존성을 제거하고 프로그래밍 언어와 환경 전반에서 사용할 수 있는 공통 JSONPath 명세로 기능하는 것을 목표로 한다. 이는 하위 호환성이 항상 달성되는 것은 아님을 의미한다. 이 문서의 설계 원칙은 사용 가능하고 안정적인 JSON 쿼리 언어를 얻는다는 목적을 위태롭게 하지 않는 한, 대략적이라도 구현 간의 "합의"를 따르는 것이다.¶
JSONPath라는 용어는 XPath의 영감 때문이기도 하고, 또한 쿼리의 결과가 JSON 쿼리 인자에서 노드를 식별하는 경로들로 이루어지기 때문에 선택되었다.¶
JSONPath 쿼리가 적용되는 JSON 값은 정의상 유효한 JSON 값이다. JSON 값은 종종 JSON 텍스트를 파싱하여 구성된다.¶
JSON 텍스트를 JSON 값으로 파싱하는 것과, JSON 텍스트가 유효한 JSON을 나타내지 않을 때 발생하는 일은 이 문서에서 정의하지 않는다. [RFC8259]의 4 및 8 섹션은 JSON 텍스트의 문법에는 부합할 수 있지만, 예측 불가능한 동작을 유발할 수 있어 상호 운용 가능한 JSON 사용은 아닌 특정 상황을 식별한다. 이 문서는 이러한 상황에서 JSONPath 쿼리에 대해 예측 가능한 동작을 정의하려고 시도하지 않는다.¶
구체적으로, 섹션 2.3.1, 2.3.2, 2.3.5, 및 2.5.2의 "의미론" 하위 섹션은 고려 중인 객체 중 하나에 대한 JSON 값이 하나의 객체에 동일한 멤버 이름을 공유하는 여러 멤버를 가진 JSON 텍스트로부터 구성된 경우 ("중복 이름"; [RFC8259]의 섹션 4 참조) 예측 불가능해지는 동작을 설명한다. 또한 이름으로 자식을 선택할 때(섹션 2.3.1)와 문자열을 비교할 때 (섹션 2.3.5.2.2), 이러한 문자열은 유니코드 스칼라 값의 시퀀스라고 가정된다. 그렇지 않은 경우 동작은 예측 불가능해진다([RFC8259]의 섹션 8.2).¶
JSONPath 표현식은 쿼리 인자라고 하는 JSON 값에 적용된다. 출력은 노드 리스트이다.¶
JSONPath 표현식은 식별자 뒤에 0개 이상의 세그먼트 시퀀스가 이어지는 형태이며, 각 세그먼트는 하나 이상의 선택자를 포함한다.¶
세그먼트는 입력 값의 자식([<selectors>]) 또는
하위 노드(..[<selectors>])를 선택한다.¶
세그먼트는 예를 들어 다음과 같이 대괄호 표기법을 사용할 수 있다:¶
$['store']['book'][0]['title']¶
또는 예를 들어 다음과 같이 더 간결한 점 표기법을 사용할 수 있다:¶
$.store.book[0].title¶
대괄호 표기법에는 모든 종류의 선택자가 하나 이상(쉼표로 구분되어) 포함된다. 선택자는 다음 섹션에서 자세히 설명한다.¶
JSONPath 표현식은 대괄호 표기법과 점 표기법을 조합하여 사용할 수 있다.¶
이 문서는 대괄호 표기법을 정식으로 취급하며, 축약 점 표기법을 대괄호 표기법의 관점에서 정의한다. 예제와 설명에서는 편의상 축약형을 사용한다.¶
이름 선택자, 예: 'name'은 객체의 이름이 있는 자식을 선택한다.¶
인덱스 선택자, 예: 3은 배열의 인덱스가 지정된 자식을 선택한다.¶
표현식 [*]에서 와일드카드 *(섹션 2.3.2)는
노드의 모든 자식을 선택하고,
표현식 ..[*]에서는 노드의 모든 하위 노드를 선택한다.¶
배열 슬라이스 start:end:step(섹션 2.3.4)는
배열에서 일련의 요소를 선택하며, 시작 위치, 끝 위치 및
시작에서 끝으로 위치를 이동시키는 선택적 step 값을 제공한다.¶
필터 표현식 ?<logical-expr>은
객체 또는 배열의 특정 자식을 선택한다. 예:¶
$.store.book[?@.price < 10].title¶
표 1은 JSONPath 구문의 간략한 개요를 제공한다.¶
| 구문 요소 | 설명 |
|---|---|
$
|
루트 노드 식별자 (섹션 2.2) |
@
|
현재 노드 식별자 (섹션 2.3.5) (필터 선택자 내에서만 유효) |
[<selectors>]
|
자식 세그먼트 (섹션 2.5.1): 노드의 자식을 0개 이상 선택한다 |
.name
|
['name']의 축약형
|
.*
|
[*]의 축약형
|
..[<selectors>]
|
하위 세그먼트 (섹션 2.5.2): 노드의 하위 노드를 0개 이상 선택한다 |
..name
|
..['name']의 축약형
|
..*
|
..[*]의 축약형
|
'name'
|
이름 선택자 (섹션 2.3.1): 객체의 이름이 있는 자식을 선택한다 |
*
|
와일드카드 선택자 (섹션 2.3.2): 노드의 모든 자식을 선택한다 |
3
|
인덱스 선택자 (섹션 2.3.3): 배열의 인덱스가 지정된 자식을 선택한다(0부터) |
0:100:5
|
배열 슬라이스 선택자 (섹션 2.3.4): 배열에 대한 start:end:step |
?<logical-expr>
|
필터 선택자 (섹션 2.3.5): 논리 표현식을 사용하여 특정 자식을 선택한다 |
length(@.foo)
|
함수 확장 (섹션 2.4): 필터 표현식에서 함수를 호출한다 |
이 섹션은 정보 제공용이다. JSONPath 표현식의 예제를 제공한다.¶
예제는 그림 1에 표시된 간단한 JSON 값을 기반으로 하며, 서점(자전거도 하나 있음)을 나타낸다.¶
{ "store": {
"book": [
{ "category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{ "category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{ "category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{ "category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 399
}
}
}
표 2는 이 예제에 적용될 수 있는 몇 가지 JSONPath 쿼리와 그 의도된 결과를 보여 준다.¶
| JSONPath | 의도된 결과 |
|---|---|
$.store.book[*].author
|
상점에 있는 모든 책의 저자 |
$..author
|
모든 저자 |
$.store.*
|
상점에 있는 모든 것, 즉 몇 권의 책과 빨간 자전거 |
$.store..price
|
상점에 있는 모든 것의 가격 |
$..book[2]
|
세 번째 책 |
$..book[2].author
|
세 번째 책의 저자 |
$..book[2].publisher
|
빈 결과: 세 번째 책에는 "publisher" 멤버가 없다 |
$..book[-1]
|
순서상 마지막 책 |
$..book[0,1]$..book[:2]
|
처음 두 권의 책 |
$..book[?@.isbn]
|
ISBN 번호가 있는 모든 책 |
$..book[?@.price<10]
|
10보다 저렴한 모든 책 |
$..*
|
입력 값에 포함된 모든 멤버 값과 배열 요소 |
JSONPath 표현식은 JSON 값 (쿼리 인자)에 적용될 때 인자의 노드를 0개 이상 선택하고 이 노드들을 노드 리스트로 출력하는 문자열이다.¶
쿼리는 UTF-8을 사용하여 인코딩되어야 한다. 이 문서에서 제시하는 쿼리 문법은 그 UTF-8 형식이 먼저 [RFC3629]에 설명된 대로 유니코드 스칼라 값으로 디코딩된다고 가정한다. 동등한 결과를 낳는 구현 접근법도 가능하다.¶
JSONPath 쿼리로 사용될 문자열은 올바른 형식이고 유효해야 한다. 문자열이 이 문서의 ABNF 구문에 부합하면 올바른 형식의 JSONPath 쿼리이다. 올바른 형식의 JSONPath 쿼리가 이 문서가 제시하는 두 가지 의미론적 요구사항도 모두 충족하면 유효하며, 그 요구사항은 다음과 같다:¶
JSONPath 구현은 올바른 형식이 아니거나 유효하지 않은 모든 쿼리에 대해 오류를 발생시켜야 한다. JSONPath 쿼리의 올바른 형식성과 유효성은 쿼리가 적용되는 JSON 값과 독립적이다. 값을 대상으로 쿼리를 적용하는 동안 JSONPath 쿼리의 올바른 형식성 및 유효성과 관련된 추가 오류가 발생할 수 없다. 이는 쿼리의 올바른 형식성/유효성 오류를 실제로 데이터의 결함에서 비롯될 수 있는 불일치와 명확히 분리한다.¶
유효한 쿼리가 기대하는 구조와 데이터에서 발견되는 구조 사이의 불일치는 빈 쿼리 결과로 이어질 수 있으며, 이는 예상치 못한 것이고 어느 한쪽의 버그를 나타낼 수 있다. 따라서 JSONPath 구현은 애플리케이션 개발자가 빈 결과의 원인을 찾는 데 도움이 되는 진단 정보를 제공하고자 할 수 있다.¶
물론 구현은 JSONPath 쿼리를 실행할 때, 예를 들어 리소스 고갈 때문에 여전히 실패할 수 있지만, 이는 이 문서에서 모델링하지 않는다. 그러나 구현은 조용히 오동작해서는 안 된다. 구체적으로, 유효한 JSONPath 쿼리가 쿼리를 올바르게 처리하기에는 크기가 너무 큰 구조화된 값에 대해 평가되는 경우(예를 들어 정확한 값의 범위를 벗어나는 숫자의 처리를 요구하는 경우), 구현은 오버플로 표시를 제공해야 한다.¶
(HTTP 오류 모델에 익숙한 독자는 올바른 형식성과 유효성을 생각할 때 400 유형 오류를 떠올릴 수 있으며, 리소스 고갈 및 관련 오류를 500 유형 오류에 비교할 수 있다고 인식할 수 있다.)¶
구문적으로, JSONPath 쿼리는 루트 식별자
($)로 이루어지며, 이는 쿼리 인자의 루트 노드를 포함하는
노드 리스트를 나타내고,
그 뒤에 비어 있을 수도 있는 세그먼트 시퀀스가 따른다.¶
jsonpath-query = root-identifier segments
segments = *(S segment)
B = %x20 / ; Space
%x09 / ; Horizontal tab
%x0A / ; Line feed or New line
%x0D ; Carriage return
S = *B ; optional blank space
¶
이 문서에서 JSONPath 쿼리의 의미론은 요구되는 결과를 정의하며, 구현의 내부 작동 방식을 규정하지 않는다. 이 문서는 절차적 단계별 방식으로 의미론을 설명할 수 있지만, 이러한 설명은 모든 구현이 동일한 결과를 산출해야 한다는 의미에서만 규범적이며, 구현자가 동일한 알고리즘을 사용해야 한다는 의미는 아니다.¶
의미론은 유효한 쿼리가 값 (쿼리 인자)에 대해 실행되어 노드 리스트(즉, 값의 노드 0개 이상의 리스트)를 생성한다는 것이다.¶
쿼리는 루트 식별자 뒤에 0개 이상의 세그먼트 시퀀스가 이어지는 형태이며, 각 세그먼트는 이전 루트 식별자 또는 세그먼트의 결과에 적용되고 다음 세그먼트에 입력을 제공한다. 이러한 결과와 입력은 노드 리스트의 형태를 취한다.¶
루트 식별자로부터 결과로 나오는 노드 리스트는 단일 노드 (쿼리 인자)를 포함한다. 마지막 세그먼트로부터 결과로 나오는 노드 리스트가 쿼리의 결과로 제시된다. 특정 API에 따라, 이는 노드에 있는 JSON 값들의 배열, 노드를 참조하는 Normalized Path들의 배열, 또는 둘 다 -- 혹은 구현이 원하는 다른 표현으로 제시될 수 있다. 참고: 빈 노드 리스트는 유효한 쿼리 결과이다.¶
세그먼트는 입력 노드 리스트 안의 각 노드에 대해 차례로 동작하며, 그 결과 노드 리스트들은 파생된 입력 노드 리스트의 순서대로 이어 붙여져 세그먼트의 결과를 생성한다. 노드는 두 번 이상 선택될 수 있으며 그 횟수만큼 노드 리스트에 나타난다. 중복 노드는 제거되지 않는다.¶
구문적으로 유효한 세그먼트는 쿼리를 실행할 때 오류를 생성해서는 안 된다. 이는 배열 범위를 벗어난 인덱스를 사용하는 것처럼 오류로 간주될 수 있는 일부 작업이 단순히 더 적은 수의 노드를 선택하는 결과가 됨을 의미한다. (이 속성에 대한 추가 논의는 섹션 2.1의 도입부에서 찾을 수 있다.)¶
이 접근법의 결과로, 어떤 세그먼트든 빈 노드 리스트를 생성하면 전체 쿼리는 빈 노드 리스트를 생성한다.¶
쿼리의 의미론이 구현에 여러 가능한 순서 중 하나를 생성할 선택권을 주는 경우, 특정 구현은 같은 쿼리의 연속 실행에서 서로 다른 순서를 생성할 수 있다.¶
다음 예제를 고려하라. 쿼리 인자가
{"a":[{"b":0},{"b":1},{"c":2}]}일 때,
쿼리 $.a[*].b는 다음 노드 리스트를 선택한다(여기서는 그
값으로 표시함): 0, 1.¶
쿼리는 $ 뒤에 세 개의 세그먼트:
.a, [*], 그리고 .b가 이어진 것으로 구성된다.¶
먼저, $는 쿼리 인자만으로 이루어진
노드 리스트를 생성한다.¶
다음으로, .a는 임의의 객체 입력 노드에서 선택하며,
멤버 이름 "a"에 대응하는 입력
노드의
임의의 멤버 값의 노드를 선택한다.
결과는 다시 단일 노드를 포함하는 리스트이다:
[{"b":0},{"b":1},{"c":2}].¶
다음으로, [*]는 입력 배열 노드에서
모든 요소를 선택한다.
결과는 세 개의 노드 리스트이다: {"b":0}, {"b":1}, 그리고
{"c":2}.¶
마지막으로, .b는 멤버 이름
b를 가진 임의의 객체 입력 노드에서 선택하며, 그 이름에 대응하는
입력 노드의 멤버 값의 노드를 선택한다.
결과는 0, 1을 포함하는 리스트이다.
이는 세 리스트의 연결이다: 각각
0, 1을 포함하는 길이 1의 두 리스트와, 길이 0의 하나의 리스트이다.¶
선택자는 자식 세그먼트 (섹션 2.5.1)와 하위 세그먼트 (섹션 2.5.2) 안에만 나타난다.¶
선택자는 입력 값의 자식을 0개 이상 포함하는 노드 리스트를 생성한다.¶
객체의 자식, 배열의 자식, 또는 객체와 배열 둘 다의 자식을 생성하는 다양한 종류의 선택자가 있다.¶
selector = name-selector /
wildcard-selector /
slice-selector /
index-selector /
filter-selector
¶
각 종류의 선택자에 대한 구문과 의미론은 아래에 정의되어 있다.¶
이름 선택자 '<name>'는
최대 하나의 객체 멤버 값을 선택한다.¶
JSON과 달리, JSONPath 구문은 문자열이 작은따옴표 또는 큰따옴표로 둘러싸이는 것을 허용한다.¶
name-selector = string-literal
string-literal = %x22 *double-quoted %x22 / ; "string"
%x27 *single-quoted %x27 ; 'string'
double-quoted = unescaped /
%x27 / ; '
ESC %x22 / ; \"
ESC escapable
single-quoted = unescaped /
%x22 / ; "
ESC %x27 / ; \'
ESC escapable
ESC = %x5C ; \ backslash
unescaped = %x20-21 / ; see RFC 8259
; omit 0x22 "
%x23-26 /
; omit 0x27 '
%x28-5B /
; omit 0x5C \
%x5D-D7FF /
; skip surrogate code points
%xE000-10FFFF
escapable = %x62 / ; b BS backspace U+0008
%x66 / ; f FF form feed U+000C
%x6E / ; n LF line feed U+000A
%x72 / ; r CR carriage return U+000D
%x74 / ; t HT horizontal tab U+0009
"/" / ; / slash (solidus) U+002F
"\" / ; \ backslash (reverse solidus) U+005C
(%x75 hexchar) ; uXXXX U+XXXX
hexchar = non-surrogate /
(high-surrogate "\" %x75 low-surrogate)
non-surrogate = ((DIGIT / "A"/"B"/"C" / "E"/"F") 3HEXDIG) /
("D" %x30-37 2HEXDIG )
high-surrogate = "D" ("8"/"9"/"A"/"B") 2HEXDIG
low-surrogate = "D" ("C"/"D"/"E"/"F") 2HEXDIG
HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
¶
참고:¶
Double-quoted 문자열은 JSON 문자열 구문([RFC8259]의 섹션 7)을 따른다.
single-quoted 문자열은 유사한 패턴을 따른다.
이 구문을 개선하려는 시도는 하지 않았으므로,
U+1F041 ("🁁",
DOMINO TILE HORIZONTAL-02-02)처럼 0xFFFF보다 큰
스칼라 값을 가진 문자를 이스케이프하려는 경우,
한 쌍의 서로게이트 이스케이프로 표현해야 한다
(이 경우 "\uD83C\uDC41").¶
\u 이스케이프 안의 각 16진수 숫자(hexchar가
참조하는 규칙에 지정된 것)는 소문자 또는 대문자일 수 있지만,
\u의 u는 소문자여야 한다(%x75로 표시됨).¶
name-selector 문자열은 주변 따옴표를 제거하고
각 이스케이프 시퀀스를 그에 대응하는 유니코드 문자로 치환함으로써,
멤버 이름 M으로 변환되어야 MUST 한다. 이는
표 4에 표시된 바와 같다:¶
| 이스케이프 시퀀스 | 유니코드 문자 | 설명 |
|---|---|---|
\b
|
U+0008 | BS 백스페이스 |
\t
|
U+0009 | HT 수평 탭 |
\n
|
U+000A | LF 줄 바꿈 |
\f
|
U+000C | FF 폼 피드 |
\r
|
U+000D | CR 캐리지 리턴 |
\"
|
U+0022 | 따옴표 |
\'
|
U+0027 | 아포스트로피 |
\/
|
U+002F | 슬래시(solidus) |
\\
|
U+005C | 역슬래시(reverse solidus) |
\uXXXX
|
섹션 2.3.1.1 참조 | 16진수 이스케이프 |
name-selector를 객체 노드에 적용하면,
이름이 멤버 이름 M과 같은 멤버 값을 선택하거나,
그러한 멤버 값이 없으면 아무것도 선택하지 않는다.
객체가 아닌 값에서는 아무것도 선택되지 않는다.¶
참고: 이름 선택자를 처리하려면
멤버 이름 문자열 M과 선택자가 적용되는 JSON 안의
멤버 이름 문자열을 비교해야 한다.
두 문자열은 유니코드 스칼라 값의 동일한 시퀀스인 경우에만
같다고 간주되어야 MUST 한다.
다시 말해, 비교 전에 JSONPath의 멤버 이름 문자열
M이나 JSON 안의 멤버 이름 문자열 어느 쪽에도
정규화 작업을 적용해서는 MUST NOT 안 된다.¶
와일드카드 선택자는 객체 또는 배열의 모든 자식 노드를 선택한다. 객체의 자식이 결과 노드 리스트에 나타나는 순서는 규정되어 있지 않다. JSON 객체는 순서가 없기 때문이다. 배열의 자식은 결과 노드 리스트에 배열 순서로 나타난다.¶
객체의 자식은 그 멤버 이름이 아니라 멤버 값임에 유의하라.¶
와일드카드 선택자는 primitive JSON 값
(즉, 숫자, 문자열, true, false, 또는
null)에서는 아무것도 선택하지 않는다.¶
JSON:¶
{
"o": {"j": 1, "k": 2},
"a": [5, 3]
}
¶
쿼리:¶
표 6의 예제는 와일드카드 선택자가 자식 세그먼트에서 사용되는 모습을 보여 준다.¶
| 쿼리 | 결과 | 결과 경로 | 설명 |
|---|---|---|---|
$[*]
|
{"j": 1, "k": 2} [5, 3]
|
$['o'] $['a']
|
객체 값 |
$.o[*]
|
1 2
|
$['o']['j'] $['o']['k']
|
객체 값 |
$.o[*]
|
2 1
|
$['o']['k'] $['o']['j']
|
대체 결과 |
$.o[*, *]
|
1 2 2 1
|
$['o']['j'] $['o']['k'] $['o']['k'] $['o']['j']
|
비결정적 순서 |
$.a[*]
|
5 3
|
$['a'][0] $['a'][1]
|
배열 멤버 |
위의 쿼리 $.o[*, *] 예제는
와일드카드 선택자가 둘 이상의 멤버를 가진 객체 노드에 적용될 때,
그것이 자식 세그먼트에 나타날 때마다 서로 다른
순서의 노드 리스트를 생성할 수 있음을 보여 준다
(그러나 멤버가 두 개 미만인 객체 노드나 배열 노드에 적용될 때는 그렇지 않다).¶
인덱스 선택자 <index>는
최대 하나의 배열 요소 값과 일치한다.¶
index-selector = int ; decimal integer
int = "0" /
(["-"] DIGIT1 *DIGIT) ; - optional
DIGIT1 = %x31-39 ; 1-9 non-zero digit
¶
숫자 index-selector를 적용하면
해당하는 요소를 선택한다.
JSONPath는 그것이 음수가 되는 것을 허용한다(섹션 2.3.3.2 참조).¶
유효하려면, 인덱스 선택자 값은 I-JSON의 정확한 값 범위 안에 있어야 MUST 한다(섹션 2.1 참조).¶
참고:¶
배열에 적용된 음이 아닌 index-selector는
0부터 시작하는 인덱스를 사용해 배열 요소를 선택한다.
예를 들어 선택자 0은 첫 번째 요소를 선택하고,
선택자 4는 충분히 긴 배열의 다섯 번째 요소를 선택한다.
인덱스가 배열의 범위 밖에 있으면 아무것도 선택되지 않으며,
이는 오류가 아니다. 배열이 아닌 값에서는 아무것도 선택되지 않는다.¶
음수 index-selector는 배열 끝에서 뒤쪽으로
센다. 배열 길이를 음수 인덱스에 더하여
동등한 음이 아닌 index-selector를 얻는다.
예를 들어 선택자 -1은 마지막 요소를 선택하고,
선택자 -2는 적어도 두 개의 요소가 있는 배열의
끝에서 두 번째 요소를 선택한다.
음이 아닌 인덱스와 마찬가지로, 그러한 요소가 존재하지 않더라도
오류가 아니다. 이는 단순히 어떤 요소도 선택되지 않음을 의미한다.¶
배열 슬라이스 선택자는
<start>:<end>:<step> 형태를 가진다.
이는 배열에서 인덱스 <start>에서 시작하여
<end>에서 끝나는(단,
포함하지 않는) 요소들과 일치하며, 기본값이 1인 step만큼 증가한다.¶
slice-selector = [start S] ":" S [end S] [":" [S step ]] start = int ; included in selection end = int ; not included in selection step = int ; default: 1¶
슬라이스 선택자는 콜론으로 구분된 세 개의 선택적 10진 정수로 이루어진다. 세 번째 정수가 생략될 때 두 번째 콜론은 생략할 수 있다.¶
유효하려면, 제공되는 정수는 I-JSON의 정확한 값 범위 안에 있어야 MUST 한다(섹션 2.1 참조).¶
슬라이스 선택자는 출시되지 않은 ECMAScript 4(ES4)를 위해 제안되었던 슬라이스 연산자와 Python의 슬라이스 연산자에서 영감을 받았다.¶
이 섹션은 정보 제공용이다.¶
배열 슬라이싱은 ECMA-262 표준
[ECMA-262]에 정의된
JavaScript 언어의
Array.prototype.slice 메서드 동작에서 영감을 받았으며,
Python 슬라이스 표현식에서 영감을 받은 step 매개변수가 추가되었다.¶
배열 슬라이스 표현식
start:end:step은 인덱스 start에서 시작하여,
step만큼 증가하고, end에서 끝나는
(그 자체는 제외되는) 인덱스의 요소를 선택한다.
따라서 예를 들어 표현식 1:3(여기서
step은 기본값 1)은
인덱스 1과 2의 요소를 (그 순서로)
선택하지만,
1:5:2는 인덱스 1과
3의 요소를 선택한다.¶
step이 음수이면 요소는
역순으로 선택된다. 따라서
예를 들어 5:1:-2는 인덱스
5와 3의 요소를 (그
순서로) 선택하고, ::-1은 배열의 모든 요소를
역순으로 선택한다.¶
step이 0이면,
어떤 요소도 선택되지 않는다.
(이는 Python의 동작과 다른 한 가지 경우이며, Python은 이 경우 오류를 발생시킨다.)¶
다음 섹션은 JavaScript 또는 Python 동작에 의존하지 않고 이 동작을 완전하게 명시한다.¶
슬라이스 표현식은
step 매개변수의 부호에 따라,
입력 배열의 요소 부분집합을 배열과 같은 순서 또는 역순으로 선택한다.
배열이 아닌 노드에서는 어떤 노드도 선택하지 않는다.¶
슬라이스는 두 슬라이스 매개변수
start와 end, 그리고
반복 델타 step으로 정의된다.
이 매개변수들은 각각
선택 사항이다. 이 섹션의 나머지에서 len은
입력 배열의 길이를 나타낸다.¶
step의 기본값은
1이다.
start와 end의 기본값은
step의 부호에 따라 달라지며,
표 8에 표시되어
있다.¶
| 조건 | start | end |
|---|---|---|
| step >= 0 | 0 | len |
| step < 0 | len - 1 | -len - 1 |
슬라이스 표현식 매개변수 start와
end는 슬라이스 경계로 직접 사용할 수 없으며,
먼저 정규화되어야 한다.
이를 위한 정규화는 다음과 같이 정의된다:¶
FUNCTION Normalize(i, len):
IF i >= 0 THEN
RETURN i
ELSE
RETURN len + i
END IF
¶
길이 len인 배열에
적용된 배열 인덱스 표현식
i의 결과는 배열 슬라이싱 표현식
Normalize(i, len):Normalize(i, len)+1:1의 결과이다.¶
슬라이스 표현식 매개변수 start와
end는 슬라이스 경계 lower와
upper를 도출하는 데 사용된다.
step의 부호로 정의되는 반복 방향은
어느 매개변수가 하한이고 어느 매개변수가
상한인지를 결정한다:¶
FUNCTION Bounds(start, end, step, len):
n_start = Normalize(start, len)
n_end = Normalize(end, len)
IF step >= 0 THEN
lower = MIN(MAX(n_start, 0), len)
upper = MIN(MAX(n_end, 0), len)
ELSE
upper = MIN(MAX(n_start, -1), len-1)
lower = MIN(MAX(n_end, -1), len-1)
END IF
RETURN (lower, upper)
¶
슬라이스 표현식은 하한과
상한 사이의 인덱스를 가진 요소를 선택한다.
다음 의사 코드에서 a(i)는 배열 a의
i+1번째 요소이다
(즉, a(0)은 첫 번째 요소이고, a(1)은
두 번째 요소이며, 이런 식으로 이어진다).¶
IF step > 0 THEN
i = lower
WHILE i < upper:
SELECT a(i)
i = i + step
END WHILE
ELSE if step < 0 THEN
i = upper
WHILE lower < i:
SELECT a(i)
i = i + step
END WHILE
END IF
¶
step = 0이면 어떤 요소도
선택되지 않으며, 결과 배열은 비어 있다.¶
JSON:¶
["a", "b", "c", "d", "e", "f", "g"]¶
쿼리:¶
표 9의 예제는 배열 슬라이스 선택자가 자식 세그먼트에서 사용되는 모습을 보여 준다.¶
| 쿼리 | 결과 | 결과 경로 | 설명 |
|---|---|---|---|
$[1:3]
|
"b" "c"
|
$[1] $[2]
|
기본 step이 있는 슬라이스 |
$[5:]
|
"f" "g"
|
$[5] $[6]
|
끝 인덱스가 없는 슬라이스 |
$[1:5:2]
|
"b" "d"
|
$[1] $[3]
|
step 2가 있는 슬라이스 |
$[5:1:-2]
|
"f" "d"
|
$[5] $[3]
|
음수 step이 있는 슬라이스 |
$[::-1]
|
"g" "f" "e"
"d" "c" "b" "a"
|
$[6] $[5] $[4]
$[3] $[2] $[1] $[0]
|
역순 슬라이스 |
필터 선택자는 구조화된 값, 즉 JSON 배열과 객체의 요소 또는 멤버를 반복 처리하는 데 사용된다. 구조화된 값은 필터 선택자를 사용하는 자식 또는 하위 세그먼트가 제공하는 노드 리스트에서 식별된다.¶
각 반복(요소/멤버)마다 논리 표현식 (필터 표현식)이 평가되며, 이는 해당 요소/멤버의 노드가 선택될지를 결정한다. (논리 표현식은 수학적으로는 Boolean 값에 해당하는 것으로 평가되지만, 이 명세는 JSON이 표현할 수 있는 Boolean 값과의 구분을 유지하기 위해 logical이라는 용어를 사용한다.)¶
반복 처리 과정에서 필터 표현식은 필터링되는 구조화된 값의 각 배열 요소 또는 객체 멤버 값의 노드를 받는다. 이 요소 또는 멤버 값은 이후 현재 노드로 알려진다.¶
현재 노드는 필터 표현식의 하위 표현식에서
하나 이상의 JSONPath 쿼리의 시작으로 사용될 수 있으며,
current-node-identifier @를 통해 표기된다.
각 JSONPath 쿼리는 쿼리 결과의 존재 여부를 검사하거나,
그 쿼리에서 나온 특정 JSON 값을 얻어 비교에 사용하거나,
함수 인자로 사용할 수 있다.¶
필터 선택자는 함수 확장을 사용할 수 있으며, 이는 섹션 2.4에서 다룬다. 필터 선택자의 논리 표현식 안에서 함수 표현식은 노드 리스트와 값에 대해 동작하는 데 사용될 수 있다. 사용할 수 있는 함수 집합은 확장 가능하며, 여러 함수가 사전 정의되어 있고(섹션 2.4 참조), "Function Extensions" 하위 레지스트리(섹션 3.2)가 추가 함수를 등록할 수 있는 기능을 제공한다. 함수가 정의되면 고유한 이름이 주어지고, 그 반환 값과 각 매개변수에는 선언 타입이 주어진다. 타입 시스템은 범위가 제한되어 있으며, 그 목적은 함수가 없을 때 필터 표현식의 문법에 암묵적으로 포함되는 제한을 표현하는 것이다. 타입 시스템은 또한 함수 표현식을 사용하지 않을 때 문법에서 다양한 종류의 표현식이 처리되는 방식을 모방하는 변환(섹션 2.4.2)을 안내한다.¶
필터 선택자는
?<logical-expr> 형태를 가진다.¶
filter-selector = "?" S logical-expr¶
필터 표현식은 부작용이 없는 구성요소로
이루어져 있으므로,
평가 순서는 정의될 필요가 없으며 정의되어 있지 않다.
마찬가지로, 논리곱(&&)과 논리합
(||)(나중에 정의됨)에 대해서도,
단락 평가 구현과 완전 평가 구현 모두
같은 결과로 이어진다. 따라서 두 구현 전략 모두 유효하다.¶
현재 노드는 현재 노드 식별자 @를 통해
접근할 수 있다.
이 식별자는 식별자를 직접 둘러싸고 있는 filter-selector의
현재 노드를 가리킨다. 참고: 중첩된 filter-selector 안에서는,
식별자를 직접 둘러싸고 있는 filter-selector가 아닌 다른
filter-selector의 현재 노드를 가리키는 구문이 없다
(즉, 식별자를 직접 둘러싸고 있는 filter-selector를 둘러싸는
filter-selector의 현재 노드를 가리킬 수 없다).¶
논리 표현식은 일반적인 Boolean 연산자
(||는 OR,
&&는 AND, !는 NOT)를 제공한다.
이들은 Boolean 대수의 일반 의미론을 가지며 그 법칙을 따른다
(예를 들어 [BOOLEAN-LAWS]
참조).
괄호는 그룹화를 위해
logical-expr 안에서 사용될 수 MAY 있다.¶
logical-expr가
괄호로 둘러싸인 표현식으로 이루어질 필요는 없다
(이는 [JSONPath-orig]에서 요구되었다).
다만 그렇게 할 수 있으며, 의미론은 괄호가 없을 때와 같다.¶
logical-expr = logical-or-expr
logical-or-expr = logical-and-expr *(S "||" S logical-and-expr)
; disjunction
; binds less tightly than conjunction
logical-and-expr = basic-expr *(S "&&" S basic-expr)
; conjunction
; binds more tightly than disjunction
basic-expr = paren-expr /
comparison-expr /
test-expr
paren-expr = [logical-not-op S] "(" S logical-expr S ")"
; parenthesized expression
logical-not-op = "!" ; logical NOT operator
¶
테스트 표현식은
내장 쿼리가 지정한 노드의 존재를 검사하거나(섹션
2.3.5.2.1 참조),
함수 표현식의 결과를 검사한다(섹션 2.4 참조).
후자의 경우, 함수의 선언된 결과 타입이
LogicalType이면(섹션
2.4.1 참조) 결과가
LogicalTrue인지 검사하고, 함수의 선언된 결과 타입이
NodesType이면 결과가 비어 있지 않은지 검사한다.
함수의 선언된 결과 타입이 ValueType이면, 테스트 표현식에서의 사용은
올바른 타입이 아니다(섹션 2.4.3
참조).¶
test-expr = [logical-not-op S]
(filter-query / ; existence/non-existence
function-expr) ; LogicalType or NodesType
filter-query = rel-query / jsonpath-query
rel-query = current-node-identifier segments
current-node-identifier = "@"
¶
비교 표현식은 primitive 값
(즉, 숫자, 문자열, true, false, 그리고
null) 사이의 비교에 사용할 수 있다.
이 값들은 리터럴 값, 최대 하나의 노드를 선택하고
그 노드의 값이 사용되는 단일 쿼리, 또는
ValueType 타입의 함수 표현식(섹션
2.4 참조)을 통해 얻을 수 있다.¶
comparison-expr = comparable S comparison-op S comparable
literal = number / string-literal /
true / false / null
comparable = literal /
singular-query / ; singular query value
function-expr ; ValueType
comparison-op = "==" / "!=" /
"<=" / ">=" /
"<" / ">"
singular-query = rel-singular-query / abs-singular-query
rel-singular-query = current-node-identifier singular-query-segments
abs-singular-query = root-identifier singular-query-segments
singular-query-segments = *(S (name-segment / index-segment))
name-segment = ("[" name-selector "]") /
("." member-name-shorthand)
index-segment = "[" index-selector "]"
¶
리터럴은 JSON에서 일반적인 방식으로 표기할 수 있다 (문자열이 작은따옴표 구분자를 사용할 수 있다는 확장이 있다).¶
참고: 따옴표로 묶인 문자열의 알파벳 문자는 ABNF에서 대소문자를 구분하지 않으므로, 부동소수점 숫자 안에서 ABNF 표현식 "e"는 문자 'e' 또는 'E'일 수 있다.¶
true, false, 그리고
null은 소문자만 허용된다(대소문자 구분).¶
number = (int / "-0") [ frac ] [ exp ] ; decimal number frac = "." 1*DIGIT ; decimal fraction exp = "e" [ "-" / "+" ] 1*DIGIT ; decimal exponent true = %x74.72.75.65 ; true false = %x66.61.6c.73.65 ; false null = %x6e.75.6c.6c ; null¶
표 10은 필터 표현식 연산자를 우선순위가 높은 것 (가장 강하게 결합함)부터 낮은 것(가장 약하게 결합함)까지 순서대로 나열한다.¶
| 우선순위 | 연산자 타입 | 구문 |
|---|---|---|
| 5 | 그룹화 함수 표현식 |
(...) name (...)
|
| 4 | 논리 NOT |
!
|
| 3 | 관계 |
== !=<
<= > >=
|
| 2 | 논리 AND |
&&
|
| 1 | 논리 OR |
||
|
필터 선택자는 배열과 객체에만 동작한다. 그 결과는 각각 배열 요소 또는 멤버 값의 (0개, 1개, 여러 개, 또는 모두) 리스트이다. primitive 값에 적용되면 아무것도 선택하지 않는다(따라서 필터 선택자의 결과에 기여하지 않는다).¶
결과 노드 리스트에서 배열의 자식은 배열 내 위치에 따라 정렬된다. 객체의 자식(배열과 달리)이 결과 노드 리스트에 나타나는 순서는 규정되어 있지 않다. JSON 객체는 순서가 없기 때문이다.¶
논리 맥락에서 쿼리 자체는 존재성 테스트이며, 쿼리가 하나 이상의 노드를 선택하면 true를 산출하고 어떤 노드도 선택하지 않으면 false를 산출한다.¶
존재성 테스트는 다음 점에서 비교와 다르다:¶
쿼리가 선택한 노드의 값을 검사하려면
명시적 비교가 필요하다.
예를 들어 쿼리 @.foo가 선택한 노드의 값이
null인지 테스트하려면,
부정 존재성 테스트 !@.foo(@.foo가 노드를 선택하면
노드의 값과 관계없이 false를 산출함) 대신
@.foo == null을 사용한다(섹션 2.6 참조).
마찬가지로, @.foo == false는
@.foo가 노드를 선택하고
그 노드의 값이 false인 경우에만 true를 산출한다.¶
비교 연산자 ==와
<가 먼저 정의되며, 그다음 이것들을 사용하여
!=, <=, >, 그리고
>=가 정의된다.¶
비교의 어느 한쪽이
빈 노드 리스트 또는 특수 결과 Nothing
(섹션 2.4.1 참조)을 결과로 낼
때:¶
==를 사용하는
비교는 다른 쪽도 빈 노드 리스트 또는 특수 결과
Nothing을 결과로 내는 경우에만 true를 산출한다.¶
<를 사용하는 비교는 false를 산출한다.¶
비교의 어느 한쪽에 있는 쿼리나 함수 표현식이 단일 노드로 이루어진 노드 리스트를 결과로 내면, 그쪽은 해당 노드의 값으로 치환되고, 그런 다음:¶
연산자
==를 사용하는 비교는 비교가 다음 사이에서 이루어지는 경우에만
true를 산출한다:¶
연산자
<를 사용하는 비교는
비교가 둘 다 숫자이거나 둘 다 문자열인 값 사이에서 이루어지고,
그 비교를 만족하는 경우에만 true를 산출한다:¶
!=, <=,
>, 그리고 >=는
다른 비교 연산자의 관점에서 정의된다. 임의의 a와 b에 대해:¶
첫 번째 예제 집합은 주어진 JSON 값을 입력으로 할 때 몇 가지 비교 표현식과 그 결과를 보여 준다.¶
JSON:¶
{
"obj": {"x": "y"},
"arr": [2, 3]
}
¶
비교:¶
| 비교 | 결과 | 설명 |
|---|---|---|
$.absent1 == $.absent2
|
true | 빈 노드 리스트 |
$.absent1 <= $.absent2
|
true |
==는 <=를 함의함
|
$.absent == 'g'
|
false | 빈 노드 리스트 |
$.absent1 != $.absent2
|
false | 빈 노드 리스트 |
$.absent != 'g'
|
true | 빈 노드 리스트 |
1 <= 2
|
true | 숫자 비교 |
1 > 2
|
false | 숫자 비교 |
13 == '13'
|
false | 타입 불일치 |
'a' <= 'b'
|
true | 문자열 비교 |
'a' > 'b'
|
false | 문자열 비교 |
$.obj == $.arr
|
false | 타입 불일치 |
$.obj != $.arr
|
true | 타입 불일치 |
$.obj == $.obj
|
true | 객체 비교 |
$.obj != $.obj
|
false | 객체 비교 |
$.arr == $.arr
|
true | 배열 비교 |
$.arr != $.arr
|
false | 배열 비교 |
$.obj == 17
|
false | 타입 불일치 |
$.obj != 17
|
true | 타입 불일치 |
$.obj <= $.arr
|
false | 객체와 배열은
< 비교를 제공하지 않음
|
$.obj < $.arr
|
false | 객체와 배열은
< 비교를 제공하지 않음
|
$.obj <= $.obj
|
true |
==는 <=를 함의함
|
$.arr <= $.arr
|
true |
==는 <=를 함의함
|
1 <= $.arr
|
false | 배열은
< 비교를 제공하지 않음
|
1 >= $.arr
|
false | 배열은
< 비교를 제공하지 않음
|
1 > $.arr
|
false | 배열은
< 비교를 제공하지 않음
|
1 < $.arr
|
false | 배열은
< 비교를 제공하지 않음
|
true <= true
|
true |
==는 <=를 함의함
|
true > true
|
false | Boolean은
< 비교를 제공하지 않음
|
두 번째 예제 집합은 필터 선택자를 사용하는 몇 가지 완전한 JSONPath 쿼리와, 주어진 JSON 값을 입력으로 하여 이러한 쿼리를 평가한 결과를 보여 준다. (참고: 두 쿼리는 함수 확장을 사용한다. 이에 대한 자세한 내용은 섹션 2.4.6과 섹션 2.4.7을 참조하라.)¶
JSON:¶
{
"a": [3, 5, 1, 2, 4, 6,
{"b": "j"},
{"b": "k"},
{"b": {}},
{"b": "kilo"}
],
"o": {"p": 1, "q": 2, "r": 3, "s": 5, "t": {"u": 6}},
"e": "f"
}
¶
쿼리:¶
표 12의 예제는 필터 선택자가 자식 세그먼트에서 사용되는 모습을 보여 준다.¶
| 쿼리 | 결과 | 결과 경로 | 설명 |
|---|---|---|---|
$.a[?@.b == 'kilo']
|
{"b": "kilo"}
|
$['a'][9]
|
멤버 값 비교 |
$.a[?(@.b == 'kilo')]
|
{"b": "kilo"}
|
$['a'][9]
|
괄호로 둘러싼 동등한 쿼리 |
$.a[?@>3.5]
|
5 4 6
|
$['a'][1] $['a'][4] $['a'][5]
|
배열 값 비교 |
$.a[?@.b]
|
{"b": "j"} {"b": "k"} {"b": {}} {"b": "kilo"}
|
$['a'][6] $['a'][7] $['a'][8] $['a'][9]
|
배열 값 존재성 |
$[?@.*]
|
[3, 5, 1, 2, 4, 6, {"b": "j"}, {"b": "k"}, {"b": {}}, {"b": "kilo"}]
{"p": 1, "q": 2, "r": 3, "s": 5, "t": {"u": 6}}
|
$['a'] $['o']
|
비단일 쿼리의 존재성 |
$[?@[?@.b]]
|
[3, 5, 1, 2, 4, 6, {"b": "j"}, {"b": "k"}, {"b": {}}, {"b": "kilo"}]
|
$['a']
|
중첩 필터 |
$.o[?@<3, ?@<3]
|
1 2 2 1
|
$['o']['p'] $['o']['q'] $['o']['q'] $['o']['p']
|
비결정적 순서 |
$.a[?@<2 || @.b == "k"]
|
1 {"b": "k"}
|
$['a'][2] $['a'][7]
|
배열 값 논리 OR |
$.a[?match(@.b, "[jk]")]
|
{"b": "j"} {"b": "k"}
|
$['a'][6] $['a'][7]
|
배열 값 정규 표현식 매치 |
$.a[?search(@.b, "[jk]")]
|
{"b": "j"} {"b": "k"} {"b": "kilo"}
|
$['a'][6] $['a'][7] $['a'][9]
|
배열 값 정규 표현식 검색 |
$.o[?@>1 && @<4]
|
2 3
|
$['o']['q'] $['o']['r']
|
객체 값 논리 AND |
$.o[?@>1 && @<4]
|
3 2
|
$['o']['r'] $['o']['q']
|
대체 결과 |
$.o[?@.u || @.x]
|
{"u": 6}
|
$['o']['t']
|
객체 값 논리 OR |
$.a[?@.b == $.x]
|
3 5 1 2 4 6
|
$['a'][0] $['a'][1] $['a'][2] $['a'][3] $['a'][4] $['a'][5]
|
값이 없는 쿼리들의 비교 |
$.a[?@ == @]
|
3 5 1 2 4 6 {"b": "j"} {"b": "k"} {"b": {}} {"b": "kilo"}
|
$['a'][0] $['a'][1]
$['a'][2] $['a'][3]
$['a'][4] $['a'][5]
$['a'][6] $['a'][7]
$['a'][8] $['a'][9]
|
primitive 값과 구조화된 값의 비교 |
위의 쿼리
$.o[?@<3, ?@<3] 예제는 필터 선택자가
자식 세그먼트에 나타날 때마다 서로 다른 순서의
노드 리스트를 생성할 수 있음을 보여 준다.¶
앞선 하위 섹션들에서 정의된 필터 표현식 기능을 넘어, JSONPath는 필터 표현식 기능을 추가하는 데 사용할 수 있는 확장 지점인 "함수 확장"을 정의한다.¶
이 섹션은 확장 지점과 이 확장 지점을 사용하는 몇 가지 함수 확장을 정의한다. 이러한 메커니즘은 확장 지점을 사용하도록 설계되었지만, JSONPath 명세의 필수적인 부분이며 이 명세의 다른 필수적인 부분과 마찬가지로 구현될 것으로 기대된다.¶
함수 확장은 등록된 이름(섹션 3.2 참조)을 정의하며, 이 이름은 0개 이상의 인수 시퀀스에 적용되어 결과를 생성할 수 있다. 등록된 각 함수 이름은 고유하다.¶
함수 확장은 그 평가가 부작용이 없도록 정의되어야 MUST 한다. 즉, 이를 포함하는 표현식에 대해 가능한 모든 평가 순서와 단락 평가 또는 완전 평가의 선택이 동일한 결과로 이어져야 MUST 한다. (참고: 메모이제이션이나 로깅은 이 의미에서 부작용이 아니다. 이는 구현 수준에서만 보이며, 평가 결과에 영향을 주지 않기 때문이다.)¶
function-name = function-name-first *function-name-char
function-name-first = LCALPHA
function-name-char = function-name-first / "_" / DIGIT
LCALPHA = %x61-7A ; "a".."z"
function-expr = function-name "(" S [function-argument
*(S "," S function-argument)] S ")"
function-argument = literal /
filter-query / ; (includes singular-query)
logical-expr /
function-expr
¶
쿼리 안의 모든 함수 표현식은 위의 ABNF에 부합하여 올바른 형식이어야 하고 타입이 올바라야 한다. 그렇지 않으면 JSONPath 구현은 오류를 발생시켜야 MUST 한다 (섹션 2.1 참조). 어떤 함수 표현식이 타입이 올바른지를 정의하기 위해, 먼저 타입 시스템을 도입한다.¶
함수 확장의 각 매개변수와 결과는 선언된 타입을 가져야 한다.¶
선언된 타입은 JSONPath 쿼리가 적용되는 쿼리 인자와 독립적으로 JSONPath 쿼리의 타입 올바름을 검사할 수 있게 한다.¶
표 13은 사용 가능한 타입을 그들이 포함하는 인스턴스의 관점에서 정의한다.¶
| 타입 | 인스턴스 |
|---|---|
ValueType
|
JSON 값 또는
Nothing
|
LogicalType
|
LogicalTrue 또는 LogicalFalse
|
NodesType
|
노드 리스트 |
참고:¶
하나 이상의 노드 존재 여부를 검사함으로써 쿼리를
논리 표현식에서 사용할 수 있는 것처럼
(섹션
2.3.5.2.1), 선언 타입이 NodesType인 함수 표현식은
선언 타입이 LogicalType인 매개변수에 대한 함수 인자로
사용할 수 있으며, 그와 동등한 변환 규칙은 다음과 같다:¶
참고:¶
NodesType에서
ValueType으로의 암시적 변환은
예상 밖일 수 있으며 따라서 정의되지 않았다.¶
NodesType인 함수 표현식은
NodesType 타입의 매개변수를 받아
ValueType 타입의 결과를 반환하는 value()와 같은
함수 확장 호출로 표현식을 감싸는 방식으로,
선언 타입이 ValueType인 매개변수의 인자로 간접적으로 사용될 수 있다
(섹션 2.4.8 참조).¶
이제 함수 표현식의 타입 올바름은 이 타입 시스템의 관점에서 정의될 수 있다.¶
함수 표현식이 타입이 올바르려면:¶
그 선언 타입은 그것이 나타나는 맥락에서 타입이 올바라야 한다.¶
문법에 따르면, 함수 표현식은 세 가지 서로 다른 직접 맥락에 나타날 수 있으며, 이는 타입 올바름에 대해 다음 조건으로 이어진다:¶
그 인수는 대응하는 매개변수의 선언 타입에 대해 타입이 올바라야 한다.¶
함수 표현식의 인수는 함수의 각 인수가 대응하는 매개변수의 선언 타입에 대해 다음 조건 중 하나에 따라 사용될 수 있을 때 타입이 올바르다:¶
length() 함수 확장
length() 함수 확장은
값의 길이를 계산하고 이를 필터 표현식에서
추가 처리에 사용할 수 있게 하는 방법을 제공한다:¶
$[?length(@.authors) >= 5]¶
그 유일한 인수는 ValueType의 인스턴스이다
(위 예제에서처럼 단일 쿼리에서
가져올 수도 있음). 결과 역시
ValueType의 인스턴스이다: 부호 없는 정수 또는 특수 결과
Nothing.¶
count() 함수 확장
count() 함수 확장은
노드 리스트 안의 노드 수를 얻고 이를 필터 표현식에서
추가 처리에 사용할 수 있게 하는 방법을 제공한다:¶
$[?count(@.*.author) >= 5]¶
그 유일한 인수는 노드 리스트이다. 결과는 노드 리스트 안의 노드 수를 제공하는 값 (부호 없는 정수)이다.¶
참고:¶
match() 함수 확장
match() 함수 확장은
주어진 문자열이 주어진 정규 표현식과 (전체가; 섹션 2.4.7 참조)
일치하는지 확인하는 방법을 제공한다. 이 정규 표현식은 [RFC9485]에 설명된 형식이다.¶
$[?match(@.date, "1974-05-..")]¶
그 인수들은 ValueType의 인스턴스이다
(위 예제의 첫 번째 인수처럼
단일 쿼리에서 가져올 수도 있음).
첫 번째 인수가 문자열이 아니거나 두 번째 인수가
[RFC9485]에 부합하는 문자열이 아니면,
결과는 LogicalFalse이다.
그렇지 않으면 첫 번째 인수인 문자열을
두 번째 인수인 문자열에 포함된 I-Regexp와 매치한다.
문자열이 I-Regexp와 일치하면 결과는 LogicalTrue이고,
그렇지 않으면 LogicalFalse이다.¶
search() 함수 확장
search() 함수 확장은
주어진 문자열이 주어진 정규 표현식과 일치하는 부분 문자열을
포함하는지 확인하는 방법을 제공한다.
이 정규 표현식은 [RFC9485]에 설명된 형식이다.¶
$[?search(@.author, "[BR]ob")]¶
그 인수들은 ValueType의 인스턴스이다
(위 예제의 첫 번째 인수처럼
단일 쿼리에서 가져올 수도 있음).
첫 번째 인수가 문자열이 아니거나 두 번째 인수가
[RFC9485]에 부합하는 문자열이 아니면,
결과는 LogicalFalse이다.
그렇지 않으면 첫 번째 인수인 문자열에서
두 번째 인수인 문자열에 포함된 I-Regexp와 일치하는
부분 문자열을 검색한다. 그러한 부분 문자열이 적어도 하나 존재하면
결과는 LogicalTrue이고, 그렇지 않으면 LogicalFalse이다.¶
value() 함수 확장
value() 함수 확장은
NodesType의 인스턴스를 값으로 변환하고
이를 필터 표현식에서 추가 처리에 사용할 수 있게 하는 방법을 제공한다:¶
$[?value(@..color) == "red"]¶
그 유일한 인수는 NodesType의 인스턴스이다
(위 예제에서처럼
filter-query에서 가져올 수도 있음). 결과는
ValueType의 인스턴스이다.¶
참고: 단일 쿼리는 ValueType이 예상되는 곳이면
어디서나 사용할 수 있으므로,
단일 쿼리와 함께 value() 함수 확장을 사용할 필요가 없다.¶
| 쿼리 | 설명 |
|---|---|
$[?length(@) < 3]
|
타입이 올바름 |
$[?length(@.*) < 3]
|
@.*가
비단일 쿼리이므로 타입이 올바르지 않음
|
$[?count(@.*) == 1]
|
타입이 올바름 |
$[?count(1) == 1]
|
1이
쿼리나 함수 표현식이 아니므로 타입이 올바르지 않음
|
$[?count(foo(@.*)) == 1]
|
타입이 올바름. 여기서
foo()는 타입이 NodesType인 매개변수와
결과 타입 NodesType을 가진 함수 확장이다
|
$[?match(@.timezone, 'Europe/.*')]
|
타입이 올바름 |
$[?match(@.timezone, 'Europe/.*') == true]
|
LogicalType은
비교에서 사용할 수 없으므로 타입이 올바르지 않음
|
$[?value(@..color) == "red"]
|
타입이 올바름 |
$[?value(@..color)]
|
ValueType은
테스트 표현식에서 사용할 수 없으므로 타입이 올바르지 않음
|
$[?bar(@.a)]
|
임의의 선언 타입 매개변수와
결과 타입 LogicalType을 가진 함수
bar()에 대해 타입이 올바름
|
$[?bnl(@.*)]
|
선언 타입
NodesType 또는 LogicalType인 매개변수와
결과 타입 LogicalType을 가진 임의의 함수
bnl()에 대해 타입이 올바름
|
$[?blt(1==1)]
|
타입이 올바름. 여기서
blt()는 선언 타입
LogicalType인 매개변수와 결과 타입 LogicalType을
가진 함수이다
|
$[?blt(1)]
|
같은 함수
blt()에 대해 타입이 올바르지 않음. 1이
쿼리, logical-expr, 또는 함수 표현식이 아니기 때문이다
|
$[?bal(1)]
|
타입이 올바름. 여기서
bal()은 선언 타입
ValueType인 매개변수와 결과 타입 LogicalType을
가진 함수이다
|
입력 노드 리스트의 각 노드에 대해, 세그먼트는 하나 이상의 선택자를 노드에 적용하고 각 선택자의 결과를 입력 노드별 노드 리스트로 이어 붙인다. 그런 다음 이들은 입력 노드 리스트의 순서대로 이어 붙여져 하나의 세그먼트 결과 노드 리스트를 형성한다.¶
쿼리에 세그먼트가 많을수록, 결과 노드 리스트의 노드가 입력 값에서 갖는 깊이가 더 커진다는 점을 알 수 있다:¶
세그먼트에는 두 종류가 있다: 자식 세그먼트와 자손 세그먼트.¶
segment = child-segment / descendant-segment¶
각 종류의 세그먼트에 대한 구문과 의미론은 아래에 정의되어 있다.¶
자식 세그먼트는 대괄호로 둘러싸인, 쉼표로 구분된 비어 있지 않은 선택자 시퀀스로 이루어진다.¶
단일 와일드카드 또는 이름 선택자가 있는 경우를 위한 축약 표기도 제공된다.¶
child-segment = bracketed-selection /
("."
(wildcard-selector /
member-name-shorthand))
bracketed-selection = "[" S selector *(S "," S selector) S "]"
member-name-shorthand = name-first *name-char
name-first = ALPHA /
"_" /
%x80-D7FF /
; skip surrogate code points
%xE000-10FFFF
name-char = name-first / DIGIT
DIGIT = %x30-39 ; 0-9
ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
¶
.*는 wildcard-selector에서 직접
만들어진 child-segment로,
[*]의 축약형이다.¶
.<member-name>는
member-name-shorthand에서 만들어진
child-segment로,
['<member-name>']의 축약형이다.
참고: 이는 ABNF 규칙 member-name-shorthand에 지정된 특정
문자들로 구성된 멤버 이름에 대해서만 사용할 수 있다.
따라서 예를 들어 $.foo.bar는
$['foo']['bar']의 축약형이다($['foo.bar']의 축약형은 아니다).¶
자식 세그먼트는 선택자 시퀀스를 포함하며, 각 선택자는 입력 값의 자식을 0개 이상 선택한다.¶
서로 다른 종류의 선택자는 단일 자식 세그먼트 안에서 조합될 수 있다.¶
입력 노드 리스트의 각 노드에 대해, 자식 세그먼트의 결과 노드 리스트는 선택자들이 리스트에 나타나는 순서대로, 각 선택자의 노드 리스트를 이어 붙인 것이다. 참고: 둘 이상의 선택자와 일치하는 노드는 그 횟수만큼 노드 리스트에 유지된다.¶
선택자가 둘 이상의 가능한 순서로 노드 리스트를 생성할 수 있는 경우, 자식 세그먼트 안에서 그 선택자가 나타나는 각 위치는 서로 다른 순서의 노드 리스트를 생성할 수 있다.¶
요약하면, 자식 세그먼트는 입력 값의 구조 안으로 한 수준 더 내려간다.¶
자손 세그먼트는 이중 점
.. 뒤에 자식 세그먼트(대괄호 표기법 사용)가
오는 형태로 이루어진다.¶
자식 세그먼트의 축약형에 대응하는 축약 표기도 제공된다.¶
descendant-segment = ".." (bracketed-selection /
wildcard-selector /
member-name-shorthand)
¶
..*는
wildcard-selector에서 직접 만들어진
descendant-segment로, ..[*]의 축약형이다.¶
..<member-name>는
member-name-shorthand에서 만들어진
descendant-segment로,
..['<member-name>']의 축약형이다.
참고: child-segment의 유사한 축약형과 마찬가지로, 이는
ABNF 규칙 member-name-shorthand에 지정된 특정
문자들로 구성된 멤버 이름에 대해서만 사용할 수 있다.¶
참고: .. 자체만으로는 유효한
세그먼트가 아니다.¶
자손 세그먼트는 입력 값의 자손을 0개 이상 생성한다.¶
입력 노드 리스트의 각 노드에 대해, 자손 선택자는 입력 노드와 그 각 자손을 방문하며, 다음을 만족한다:¶
객체의 자식이 방문되는 순서는 규정되어 있지 않다. JSON 객체는 순서가 없기 때문이다.¶
자손 세그먼트가
..[<selectors>] 형태라고 하자(모든 축약형을
대괄호 표기법으로 변환한 뒤).
그리고 방문된 순서의 노드를 D1, ..., Dn
(n >= 1)이라고 하자.
참고: D1은 입력 값이다.¶
1 <= i <= n인 각
i에 대해, 노드 리스트 Ri는
자식 세그먼트 [<selectors>]를 노드
Di에 적용한 결과로 정의된다.¶
입력 노드 리스트의 각 노드에 대해,
자손 세그먼트의 결과는 R1,
..., Rn을 (그 순서대로) 이어 붙인 것이다.
그런 다음 이 결과들은 입력 노드 리스트 순서대로 이어 붙여져
세그먼트의 결과를 형성한다.¶
요약하면, 자손 세그먼트는 각 입력 값의 구조 안으로 한 수준 이상 내려간다.¶
JSON:¶
{
"o": {"j": 1, "k": 2},
"a": [5, 3, [{"j": 4}, {"k": 6}]]
}
¶
쿼리:¶
(네 번째 예제는 두 개의 동등한 쿼리로 표현될 수 있으며, 표 16에는 거의 동일한 두 행 대신 하나의 표 행에 표시되어 있음에 유의하라.)¶
| 쿼리 | 결과 | 결과 경로 | 설명 |
|---|---|---|---|
$..j
|
1 4
|
$['o']['j'] $['a'][2][0]['j']
|
객체 값 |
$..j
|
4 1
|
$['a'][2][0]['j'] $['o']['j']
|
대체 결과 |
$..[0]
|
5 {"j": 4}
|
$['a'][0] $['a'][2][0]
|
배열 값 |
$..[*] 또는 $..*
|
{"j": 1, "k": 2} [5, 3, [{"j": 4}, {"k": 6}]] 1
2 5 3 [{"j": 4}, {"k": 6}] {"j": 4}
{"k": 6} 4 6
|
$['o'] $['a'] $['o']['j'] $['o']['k'] $['a'][0] $['a'][1] $['a'][2] $['a'][2][0] $['a'][2][1] $['a'][2][0]['j']
$['a'][2][1]['k']
|
모든 값 |
$..o
|
{"j": 1, "k": 2}
|
$['o']
|
입력 값이 방문됨 |
$.o..[*, *]
|
1 2 2 1
|
$['o']['j'] $['o']['k'] $['o']['k'] $['o']['j']
|
비결정적 순서 |
$.a..[0, 1]
|
5 3 {"j": 4}
{"k": 6}
|
$['a'][0] $['a'][1] $['a'][2][0] $['a'][2][1]
|
여러 세그먼트 |
참고: 위의 $..[*] 및
$..* 예제에 대한 결과 순서는 보장되지 않는다.
다만 다음은 예외이다:¶
{"j": 1, "k": 2}는 1 및
2보다 먼저 나타나야 한다.¶
[5, 3, [{"j": 4}, {"k": 6}]]는 5,
3, 및 [{"j": 4}, {"k": 6}]보다 먼저 나타나야 한다.¶
5는 3보다 먼저 나타나야 하고, 3은
[{"j": 4}, {"k": 6}]보다 먼저 나타나야 한다.¶
5와 3은 {"j": 4},
4, {"k": 6}, 및 6보다 먼저 나타나야 한다.¶
[{"j": 4}, {"k": 6}]는 {"j": 4}
및 {"k": 6}보다 먼저 나타나야 한다.¶
{"j": 4}는 {"k": 6}보다 먼저 나타나야 한다.¶
{"k": 6}은 4보다 먼저 나타나야 하며,¶
4는 6보다 먼저 나타나야 한다.¶
위의 쿼리 $.o..[*, *] 예제는
선택자가 자손 세그먼트에 나타날 때마다 서로 다른 순서의
노드 리스트를 생성할 수 있음을 보여 준다.¶
위의 쿼리 $.a..[0, 1] 예제는
자식 세그먼트 [0, 1]이 각 노드에 차례대로 적용됨을 보여 준다
(노드가 선택자마다 한 번씩 방문되는 것이 아니다. 이는 이 명세에
부합하지 않는 일부 JSONPath 구현의 경우이다).¶
null의 의미론
참고: JSON null은 다른 모든 JSON 값과 동일하게 처리된다.
즉, "undefined"나 "missing"을 의미하는 것으로 간주되지 않는다.¶
JSON:¶
{"a": null, "b": [null], "c": [{}], "null": 1}
¶
쿼리:¶
| 쿼리 | 결과 | 결과 경로 | 설명 |
|---|---|---|---|
$.a
|
null
|
$['a']
|
객체 값 |
$.a[0]
|
배열로 사용된 null
|
||
$.a.d
|
객체로 사용된 null
|
||
$.b[0]
|
null
|
$['b'][0]
|
배열 값 |
$.b[*]
|
null
|
$['b'][0]
|
배열 값 |
$.b[?@]
|
null
|
$['b'][0]
|
존재성 |
$.b[?@==null]
|
null
|
$['b'][0]
|
비교 |
$.c[?@.d==null]
|
"missing" 값과의 비교 | ||
$.null
|
1
|
$['null']
|
전혀 JSON null이
아니며, 단지 멤버 이름 문자열임 |
정규화 경로는 값 안에서 노드의 위치를 나타내는 고유한 표현이며,
값 안의 노드를 고유하게 식별한다.
구체적으로, 정규화 경로는 제한된 구문(아래에 정의됨)을 가진 JSONPath 쿼리이며,
예를 들어 $['book'][3]와 같이 값에 적용될 때,
정규화 경로가 식별하는 노드만으로 이루어진 노드 리스트를
결과로 낸다.
참고: 정규화 경로는 특정 값 안에서 노드의 정체성을 나타낸다.
값 안의 특정 노드를 식별하는 정규화 경로는 정확히 하나만 존재한다.¶
노드 리스트는 JSON에서 문자열 배열로 간결하게 표현될 수 있으며, 이 문자열들은 정규화 경로이다.¶
정규화 경로는 노드 리스트의 테스트와 후처리, 예를 들어 중복 노드 제거를 단순화하는 예측 가능한 형식을 제공한다. 정규화 경로는 이 문서의 예제에서 결과 경로로 사용된다.¶
정규화 경로는 점 표기법이 아니라 정식 대괄호 표기법을 사용한다.¶
정규화 경로에서는 문자열 멤버 이름을 구분하기 위해 작은따옴표를 사용한다. 이는 정규화 경로가 큰따옴표로 구분된 문자열, 예를 들어 JSON 텍스트 안에 나타날 때 이스케이프가 필요한 문자 수를 줄인다.¶
정규화 경로에서는 특정 문자가 오직 한 가지 방식으로만 이스케이프되며, 그 밖의 모든 문자는 이스케이프되지 않는다.¶
normalized-path = root-identifier *(normal-index-segment)
normal-index-segment = "[" normal-selector "]"
normal-selector = normal-name-selector / normal-index-selector
normal-name-selector = %x27 *normal-single-quoted %x27 ; 'string'
normal-single-quoted = normal-unescaped /
ESC normal-escapable
normal-unescaped = ; omit %x0-1F control codes
%x20-26 /
; omit 0x27 '
%x28-5B /
; omit 0x5C \
%x5D-D7FF /
; skip surrogate code points
%xE000-10FFFF
normal-escapable = %x62 / ; b BS backspace U+0008
%x66 / ; f FF form feed U+000C
%x6E / ; n LF line feed U+000A
%x72 / ; r CR carriage return U+000D
%x74 / ; t HT horizontal tab U+0009
"'" / ; ' apostrophe U+0027
"\" / ; \ backslash (reverse solidus) U+005C
(%x75 normal-hexchar)
; certain values u00xx U+00XX
normal-hexchar = "0" "0"
(
("0" %x30-37) / ; "00"-"07"
; omit U+0008-U+000A BS HT LF
("0" %x62) / ; "0b"
; omit U+000C-U+000D FF CR
("0" %x65-66) / ; "0e"-"0f"
("1" normal-HEXDIG)
)
normal-HEXDIG = DIGIT / %x61-66 ; "0"-"9", "a"-"f"
normal-index-selector = "0" / (DIGIT1 *DIGIT)
; non-negative decimal integer
¶
주어진 노드를 식별하는 정규화 경로는 하나만 있을 수 있으므로,
구문은 어떤 문자를 이스케이프하고 어떤 문자를 이스케이프하지 않는지 규정한다.
따라서 normal-hexchar의 정의는 예를 들어 U+000B LINE TABULATION처럼
그대로 출력하기 쉽지 않지만 \n과 같은 표준 JSON 이스케이프가
없는 문자를 16진수로 이스케이프하도록 설계되었다.¶
IANA는 다음 미디어 타입을 등록했다 [RFC6838]:¶
application¶
jsonpath¶
N/A¶
N/A¶
binary (UTF-8)¶
RFC 9535의 보안 고려 사항 섹션 참조.¶
N/A¶
RFC 9535¶
JSON 데이터에서 쿼리를 전달해야 하는 애플리케이션¶
N/A¶
COMMON¶
N/A¶
JSONPath WG¶
IETF¶
이 명세에 따라, IANA는 새로운 "JSONPath" 레지스트리 안에 새로운 "Function Extensions" 하위 레지스트리를 만들었다. "Function Extensions" 하위 레지스트리는 "Expert Review" 정책을 가진다 ([RFC8126]의 섹션 4.5).¶
전문가에게는 일반적으로 적용 가능한 의미론을 시사하는 함수 확장 이름의 할당에 신중을 기하고, 널리 사용될 가능성이 높고 그 간결성을 잘 활용할 수 있는 함수들을 위해 그러한 이름을 남겨 두도록 지시된다. 또한 전문가는 등록자에게 명세 ([RFC8126]의 섹션 4.6)를 제공하도록 안내하라는 지시를 받지만, 예를 들어 등록 시점에는 명세가 없지만 곧 제공될 가능성이 높은 경우처럼 예외를 둘 수 있다. 전문가가 배포되어 사용 중인 함수 확장을 알게 되면, 그러한 등록이 잠재적인 향후 충돌을 피할 수 있다고 판단하는 경우 스스로 등록을 시작할 수도 있다.¶
하위 레지스트리의 각 항목은 다음을 포함해야 한다:¶
문자로 시작하고 이후에 문자, 숫자, 밑줄 문자를 포함할 수 있는
소문자 ASCII [RFC0020] 문자열
([a-z][_a-z0-9]*). 하위 레지스트리의 다른 항목은
같은 함수 이름을 가질 수 없다.¶
간단한 설명¶
이 함수 확장에 대해 예상되는 각 인수마다 하나씩, 0개 이상의 선언 타입을 쉼표로 구분한 목록¶
이 함수 확장에 대한 결과의 선언 타입¶
함수 확장에 대한 설명을 제공하는 참조 문서¶
이 하위 레지스트리의 초기 항목은 표 19에 나열되어 있다. "Change Controller" 열의 항목들은 모두 "IETF" 값을 가지며, "Reference" 열의 항목들은 모두 "RFC 9535의 섹션 2.4" 값을 가진다:¶
| 함수 이름 | 간단한 설명 | 매개변수 | 결과 |
|---|---|---|---|
| length | 문자열, 배열, 또는 객체의 길이 |
ValueType
|
ValueType
|
| count | 노드 리스트의 크기 |
NodesType
|
ValueType
|
| match | 정규 표현식 전체 매치 |
ValueType, ValueType
|
LogicalType
|
| search | 정규 표현식 부분 문자열 매치 |
ValueType, ValueType
|
LogicalType
|
| value | 노드 리스트 안의 단일 노드의 값 |
NodesType
|
ValueType
|
JSONPath에 대한 보안 고려 사항은 다음에서 비롯될 수 있다:¶
역사적으로 JSONPath는 쿼리의 일부를
기본 프로그래밍 언어 엔진, 예를 들어 JavaScript의 eval() 함수에
공급하는 방식으로 구현되는 경우가 많았다.
이 접근 방식은 인젝션 공격으로 이어지는 것으로 잘 알려져 있으며,
이러한 공격을 방지하려면 완벽한 입력 검증이 필요하다
(JSON 자체에 대한 유사한 고려 사항은
[RFC8259]의 섹션 12
참조).
대신 JSONPath 구현은 프로그래밍 언어 엔진의 파서에 의존하지 않고
쿼리의 전체 구문을 구현해야 한다.¶
가용성에 대한 공격은 특정 구현이 특정 경우에 보이는 비정상적으로 비용이 큰 런타임 성능을 유발하려고 시도할 수 있다. (해시 테이블 구현의 문제는 [RFC8949]의 섹션 10, 정규 표현식 구현의 성능 문제는 [RFC9485]의 섹션 8 참조.) 구현자는 공격자가 놀랄 만큼 높은, 어쩌면 지수적인 CPU 사용량을 유발하는 특수하게 제작된 JSONPath 쿼리나 쿼리 인자를 제출하거나, 예를 들어 자손 세그먼트의 단순한 재귀 구현을 통해 스택 오버플로를 유발할 수 있는 한, 좋은 평균 성능만으로는 충분하지 않다는 점을 인식해야 한다. 구현은 이러한 공격을 완화하기 위해 적절한 자원 관리를 갖추어야 한다.¶
JSONPath 쿼리는 정적인 경우만 있는 것이 아니라, 필터 표현식에서 비교할 인덱스 값, 멤버 이름, 또는 값을 제공하는 변수로부터 형성되는 경우가 많다. 이러한 변수는 검증되어야 하며(예: 주어진 값이 허용할 때 .name 같은 특정 구성만 형성되도록 허용), 변환되어야 한다(예: 문자열 구분자를 이스케이프). 이러한 검증과 변환을 올바르게 수행하지 않으면 예기치 않은 실패로 이어질 수 있고, 이는 특히 공격자가 값에 대한 제어권을 가지고 있는 경우 (예: 웹 양식에 값을 입력하는 방식으로) 가용성, 기밀성, 무결성 침해로 이어질 수 있다. 그 결과 생기는 공격 종류인 인젝션(예: SQL 인젝션)은 애플리케이션 보안 취약점의 주요 원인 가운데 지속적으로 발견되며, 특별한 주의를 요구한다.¶
JSONPath가 보안 메커니즘의 일부로 사용되는 경우, 공격자는 예기치 않거나 예측할 수 없는 동작을 유발하거나 JSONPath 구현 간 동작 차이를 이용하려고 시도할 수 있다.¶
예기치 않거나 예측할 수 없는 동작은 [RFC8259]에서 예측 불가능하다고 설명한 특정 구성을 가진 쿼리 인자에서 발생할 수 있다. 객체의 순서와 관련된 경우를 제외하면, [RFC7493]에 부합하는 모든 쿼리 인자에 대해 예측 가능한 동작을 기대할 수 있다.¶
다른 공격은 UTF-8 ([RFC3629]의 섹션 10 참조)과 유니코드 문자 집합 같은 기반 기술의 동작을 표적으로 삼을 수 있다.¶
이 부록은 문서 전반에서 사용된 ABNF 구절의 ABNF 문법을 모아 둔다.¶
그림 2는 JSONPath 쿼리의 구문을 정의하는 수집된 ABNF 문법을 포함한다.¶
jsonpath-query = root-identifier segments
segments = *(S segment)
B = %x20 / ; Space
%x09 / ; Horizontal tab
%x0A / ; Line feed or New line
%x0D ; Carriage return
S = *B ; optional blank space
root-identifier = "$"
selector = name-selector /
wildcard-selector /
slice-selector /
index-selector /
filter-selector
name-selector = string-literal
string-literal = %x22 *double-quoted %x22 / ; "string"
%x27 *single-quoted %x27 ; 'string'
double-quoted = unescaped /
%x27 / ; '
ESC %x22 / ; \"
ESC escapable
single-quoted = unescaped /
%x22 / ; "
ESC %x27 / ; \'
ESC escapable
ESC = %x5C ; \ backslash
unescaped = %x20-21 / ; see RFC 8259
; omit 0x22 "
%x23-26 /
; omit 0x27 '
%x28-5B /
; omit 0x5C \
%x5D-D7FF /
; skip surrogate code points
%xE000-10FFFF
escapable = %x62 / ; b BS backspace U+0008
%x66 / ; f FF form feed U+000C
%x6E / ; n LF line feed U+000A
%x72 / ; r CR carriage return U+000D
%x74 / ; t HT horizontal tab U+0009
"/" / ; / slash (solidus) U+002F
"\" / ; \ backslash (reverse solidus) U+005C
(%x75 hexchar) ; uXXXX U+XXXX
hexchar = non-surrogate /
(high-surrogate "\" %x75 low-surrogate)
non-surrogate = ((DIGIT / "A"/"B"/"C" / "E"/"F") 3HEXDIG) /
("D" %x30-37 2HEXDIG )
high-surrogate = "D" ("8"/"9"/"A"/"B") 2HEXDIG
low-surrogate = "D" ("C"/"D"/"E"/"F") 2HEXDIG
HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
wildcard-selector = "*"
index-selector = int ; decimal integer
int = "0" /
(["-"] DIGIT1 *DIGIT) ; - optional
DIGIT1 = %x31-39 ; 1-9 non-zero digit
slice-selector = [start S] ":" S [end S] [":" [S step ]]
start = int ; included in selection
end = int ; not included in selection
step = int ; default: 1
filter-selector = "?" S logical-expr
logical-expr = logical-or-expr
logical-or-expr = logical-and-expr *(S "||" S logical-and-expr)
; disjunction
; binds less tightly than conjunction
logical-and-expr = basic-expr *(S "&&" S basic-expr)
; conjunction
; binds more tightly than disjunction
basic-expr = paren-expr /
comparison-expr /
test-expr
paren-expr = [logical-not-op S] "(" S logical-expr S ")"
; parenthesized expression
logical-not-op = "!" ; logical NOT operator
test-expr = [logical-not-op S]
(filter-query / ; existence/non-existence
function-expr) ; LogicalType or NodesType
filter-query = rel-query / jsonpath-query
rel-query = current-node-identifier segments
current-node-identifier = "@"
comparison-expr = comparable S comparison-op S comparable
literal = number / string-literal /
true / false / null
comparable = literal /
singular-query / ; singular query value
function-expr ; ValueType
comparison-op = "==" / "!=" /
"<=" / ">=" /
"<" / ">"
singular-query = rel-singular-query / abs-singular-query
rel-singular-query = current-node-identifier singular-query-segments
abs-singular-query = root-identifier singular-query-segments
singular-query-segments = *(S (name-segment / index-segment))
name-segment = ("[" name-selector "]") /
("." member-name-shorthand)
index-segment = "[" index-selector "]"
number = (int / "-0") [ frac ] [ exp ] ; decimal number
frac = "." 1*DIGIT ; decimal fraction
exp = "e" [ "-" / "+" ] 1*DIGIT ; decimal exponent
true = %x74.72.75.65 ; true
false = %x66.61.6c.73.65 ; false
null = %x6e.75.6c.6c ; null
function-name = function-name-first *function-name-char
function-name-first = LCALPHA
function-name-char = function-name-first / "_" / DIGIT
LCALPHA = %x61-7A ; "a".."z"
function-expr = function-name "(" S [function-argument
*(S "," S function-argument)] S ")"
function-argument = literal /
filter-query / ; (includes singular-query)
logical-expr /
function-expr
segment = child-segment / descendant-segment
child-segment = bracketed-selection /
("."
(wildcard-selector /
member-name-shorthand))
bracketed-selection = "[" S selector *(S "," S selector) S "]"
member-name-shorthand = name-first *name-char
name-first = ALPHA /
"_" /
%x80-D7FF /
; skip surrogate code points
%xE000-10FFFF
name-char = name-first / DIGIT
DIGIT = %x30-39 ; 0-9
ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
descendant-segment = ".." (bracketed-selection /
wildcard-selector /
member-name-shorthand)
그림 3은
JSONPath 정규화 경로의 구문을 정의하는 수집된 ABNF 문법을 포함하며,
동시에 그림 2의 규칙
root-identifier, ESC, DIGIT, 및 DIGIT1을 사용한다.¶
normalized-path = root-identifier *(normal-index-segment)
normal-index-segment = "[" normal-selector "]"
normal-selector = normal-name-selector / normal-index-selector
normal-name-selector = %x27 *normal-single-quoted %x27 ; 'string'
normal-single-quoted = normal-unescaped /
ESC normal-escapable
normal-unescaped = ; omit %x0-1F control codes
%x20-26 /
; omit 0x27 '
%x28-5B /
; omit 0x5C \
%x5D-D7FF /
; skip surrogate code points
%xE000-10FFFF
normal-escapable = %x62 / ; b BS backspace U+0008
%x66 / ; f FF form feed U+000C
%x6E / ; n LF line feed U+000A
%x72 / ; r CR carriage return U+000D
%x74 / ; t HT horizontal tab U+0009
"'" / ; ' apostrophe U+0027
"\" / ; \ backslash (reverse solidus) U+005C
(%x75 normal-hexchar)
; certain values u00xx U+00XX
normal-hexchar = "0" "0"
(
("0" %x30-37) / ; "00"-"07"
; omit U+0008-U+000A BS HT LF
("0" %x62) / ; "0b"
; omit U+000C-U+000D FF CR
("0" %x65-66) / ; "0e"-"0f"
("1" normal-HEXDIG)
)
normal-HEXDIG = DIGIT / %x61-66 ; "0"-"9", "a"-"f"
normal-index-selector = "0" / (DIGIT1 *DIGIT)
; non-negative decimal integer
이 부록은 정보 제공용이다.¶
JSONPath가 고안되던 시점에, XML은 XML 문서에서 데이터를 분석, 변환, 선택적으로 추출하기 위한 강력한 도구를 사용할 수 있다는 점으로 주목받았다. [XPath]는 이러한 도구 중 하나이다.¶
2007년에, 새롭게 등장하던 JSON 커뮤니티에서 같은 종류의 문제를 해결하는 무언가의 필요성이 분명해졌다. 구체적으로는 다음을 위해서이다:¶
(참고: XPath는 2007년 이후 발전했으며, 최근 버전은 명목상 JSON 값 안에서 동작하는 것도 지원한다. 이 부록은 2007년에 사용할 수 있었던, 더 널리 사용되던 XPath 버전만을 논의한다.)¶
JSONPath는 XPath의 전반적인 느낌을 받아들이되, 동적 언어에서 JSON을 사용하는 사람에게 익숙한 구문(및 부분적인 의미론)으로 개념을 매핑한다.¶
예를 들어 JavaScript, Python, PHP 같은 널리 쓰이는 동적 프로그래밍 언어에서, XPath 표현식의 의미론은 다음과 같다:¶
/store/book[1]/title¶
이는 다음 표현식으로 실현될 수 있다:¶
x.store.book[0].title¶
또는 대괄호 표기법으로는 다음과 같다:¶
x['store']['book'][0]['title']¶
여기서 변수 x는 쿼리 인자를 담고 있다.¶
JSONPath 언어는 다음과 같이 설계되었다:¶
XPath 표현식이 XML 문서와 함께 사용되는 것과 같은 방식으로
JSONPath 표현식은 JSON 값에 적용된다.
JSONPath는 XPath의 맨 앞 /와 유사하게, 쿼리 인자의 루트 노드를
가리키기 위해 $를 사용한다.¶
JSONPath 표현식은 점 표기법
($.store.book[0].title)
또는 대괄호 표기법
($['store']['book'][0]['title'])을 사용해 계층 구조 아래로 더 이동한다.
둘 다 쿼리 표현식 안에서 XPath의 /를 대체하며,
여기서 점 표기법은 가볍지만 제한적인 구문으로 쓰이고,
대괄호 표기법은
무겁지만 더 일반적인 구문으로 쓰인다.¶
JSONPath와 XPath는 모두 와일드카드로 *를 사용한다.
..로 시작하는 JSONPath의 자손 세그먼트 표기법은 [E4X]에서 차용한 것으로,
XPath의 //와 유사하다.
배열 슬라이싱 구성 [start:end:step]은 JSONPath에 고유하며,
ECMASCRIPT 4의 [SLICE]에서 영감을 받았다.¶
필터 표현식은 다음과 같이
?<logical-expr> 구문을 통해 지원된다:¶
$.store.book[?@.price < 10].title¶
표 20은 유사한 XPath 개념과의 비교를 제공하여 표 1을 확장한다.¶
| XPath | JSONPath | 설명 |
|---|---|---|
/
|
$
|
루트 XML 요소 |
.
|
@
|
현재 XML 요소 |
/
|
. 또는 []
|
자식 연산자 |
..
|
n/a | 부모 연산자 |
//
|
..name, ..[index], ..*, 또는
..[*]
|
자손(JSONPath는 이 구문을 E4X에서 차용함) |
*
|
*
|
와일드카드: 이름과 관계없이 모든 XML 요소 |
@
|
n/a | 속성 접근: JSON 값에는 속성이 없음 |
[]
|
[]
|
XML 요소 컬렉션을 반복하고 술어에 사용되는 첨자 연산자 |
|
|
[,]
|
합집합 연산자(노드 집합의 조합을 결과로 냄); JSONPath에서는 리스트 연산자라고 하며, 멤버 이름, 배열 인덱스, 슬라이스를 결합할 수 있게 함 |
| n/a |
[start:end:step]
|
ES4에서 차용한 배열 슬라이스 연산자 |
[]
|
?
|
필터(스크립트) 표현식을 적용함 |
| seamless | n/a | 표현식 엔진 |
()
|
n/a | 그룹화 |
추가 설명을 위해, 표 21은 몇 가지 XPath 표현식과 그에 대응하는 JSONPath 표현식을 보여 준다.¶
| XPath | JSONPath | 결과 |
|---|---|---|
/store/book/author
|
$.store.book[*].author
|
store 안의 모든 책의 저자 |
//author
|
$..author
|
모든 저자 |
/store/*
|
$.store.*
|
store 안의 모든 것, 즉 몇 권의 책과 빨간 자전거 |
/store//price
|
$.store..price
|
store 안의 모든 것의 가격 |
//book[3]
|
$..book[2]
|
세 번째 책 |
//book[last()]
|
$..book[-1]
|
순서상 마지막 책 |
//book[position()<3]
|
$..book[0,1]$..book[:2]
|
처음 두 권의 책 |
//book[isbn]
|
$..book[?@.isbn]
|
ISBN 번호가 있는 모든 책을 필터링 |
//book[price<10]
|
$..book[?@.price<10]
|
10보다 저렴한 모든 책을 필터링 |
//*
|
$..*
|
XML 문서의 모든 요소; 입력 값에 포함된 모든 멤버 값과 배열 요소 |
XPath는 이 비교에 나열된 것보다 훨씬 더 많은 기능 (축약되지 않은 구문의 위치 경로, 연산자, 함수)을 가진다. 또한 XPath와 JSONPath에서 첨자 연산자가 동작하는 방식에는 상당한 차이가 있다:¶
이 부록은 정보 제공용이다.¶
JSON 포인터 [RFC6901]와 관련하여, JSONPath는 대체물이 아니라 더 강력한 동반자로 의도되었다. 두 표준의 목적은 서로 다르다.¶
JSON 포인터는 구조가 알려진 JSON 값 안에서 단일 값을 식별하기 위한 것이다.¶
JSONPath도 예를 들어 정규화 경로를 사용하여 JSON 값 안의 단일 값을 식별할 수 있다. 그러나 JSONPath는 구조가 일반적인 방식으로만 알려진 JSON 값에서 여러 값을 검색하고 추출하는 데 사용할 수 있는 쿼리 구문이기도 하다.¶
정규화 JSONPath는 어떤 JSON 값에 대한 지식 없이도 구문을 변환하여 JSON 포인터로 변환할 수 있다. 그 반대는 일반적으로 참이 아니다. 즉, JSON 포인터의 숫자 참조 토큰(경로 구성 요소)은 객체의 멤버 값이나 배열의 요소를 식별할 수 있다. JSONPath 쿼리로 변환하려면, 이러한 경우를 구별하기 위해 JSON 값의 구조에 대한 지식이 필요하다.¶
이 문서는 JSONPath를 정의한 Stefan Gössner의 원래 온라인 글 [JSONPath-orig]을 기반으로 한다.¶
책 예제는 독일 Bielefeld University가 2002년에 사용한 강의 자료에서 가져왔다.¶
이 작업은 수많은 쿼리에 적용된 40개가 넘는 JSONPath 구현의 동작을 자세히 다루는 탁월한 JSONPath 비교 프로젝트 [COMPARISON]에 대해 Christoph Burgmer에게 빚지고 있다.¶