| RFC 8785 | JSON 정규화 방식 | 2020년 6월 |
| Rundgren, et al. | Informational | [Page] |
해싱 및 서명과 같은 암호화 작업은, 해당 작업을 안정적으로 반복 가능하게 하기 위해 데이터가 불변 형식으로 표현되어야 한다. 이를 해결하는 한 가지 방법은 데이터의 정규 표현을 만드는 것이다. 정규화는 또한 데이터가 "wire"에서 원래 형식으로 교환되도록 허용하는 동시에, 생산자와 소비자 엔드포인트에서 데이터의 정규화된 대응물에 대해 수행되는 암호화 작업이 일관된 결과를 생성하도록 한다.¶
이 문서는 JSON Canonicalization Scheme(JCS)을 설명한다. 이 명세는 ECMAScript가 정의한 JSON 기본값의 엄격한 직렬화 방법을 기반으로 하고, JSON 데이터를 Internet JSON(I-JSON) 하위 집합으로 제한하며, 결정적 속성 정렬을 사용하여 JSON 데이터의 정규 표현을 만드는 방법을 정의한다.¶
이 문서는 인터넷 표준 트랙 명세가 아니며, 정보 제공 목적으로 게시된다.¶
이는 다른 어떤 RFC 스트림과도 독립적으로 RFC 시리즈에 기여한 문서이다. RFC Editor는 재량에 따라 이 문서를 게시하기로 선택했으며, 구현 또는 배포에 대한 가치에 대해서는 어떠한 진술도 하지 않는다. RFC Editor가 게시를 승인한 문서는 어떤 수준의 인터넷 표준 후보도 아니다. RFC 7841의 Section 2를 참조하라.¶
이 문서의 현재 상태, 정오표, 피드백 제공 방법에 대한 정보는 https://www.rfc-editor.org/info/rfc8785에서 얻을 수 있다.¶
Copyright (c) 2020 IETF Trust 및 문서 저자로 식별된 사람들. 모든 권리 보유.¶
이 문서는 출판일에 유효한 BCP 78 및 IETF Trust의 IETF 문서와 관련된 법적 조항 (https://trustee.ietf.org/license-info)의 적용을 받는다. 이 문서와 관련된 권리와 제한을 설명하고 있으므로 해당 문서를 주의 깊게 검토해야 한다.¶
이 문서는 JSON Canonicalization Scheme(JCS)을 설명한다. 이 명세는 ECMAScript [ECMA-262]가 정의한 JSON 기본값의 엄격한 직렬화 방법을 기반으로 하고, JSON 데이터를 I-JSON [RFC7493] 하위 집합으로 제한하며, 결정적 속성 정렬을 사용하여 JSON [RFC8259] 데이터의 정규 표현을 만드는 방법을 정의한다. JCS의 출력은 암호화 방법에서 사용할 수 있는 JSON 데이터의 "해시 가능" 표현이다. 이어지는 문단은 주요 설계 고려사항을 개략적으로 설명한다.¶
해싱 및 서명과 같은 암호화 작업은, 해당 작업을 안정적으로 반복 가능하게 하기 위해 데이터가 불변 형식으로 표현되어야 한다. 이를 달성하는 한 가지 방법은 데이터를 base64url [RFC4648]처럼 단순하고 고정된 표현을 가진 형식으로 변환하는 것이다. JSON Web Signature(JWS) [RFC7515]는 이 문제를 이런 방식으로 해결했다. 또 다른 해결책은 XML signature [XMLDSIG] 표준에서 수행된 것과 유사하게, 데이터의 정규 버전을 만드는 것이다.¶
정규화 방식의 주요 장점은 데이터를 원래 형식으로 유지할 수 있다는 점이다. 이것이 JCS의 핵심 근거이다. 달리 말하면, 정규화를 사용하면 JSON 객체가 서명된 후에도 JSON 객체로 남을 수 있다. 이는 시스템 설계, 문서화, 로깅을 단순화할 수 있다.¶
"바퀴를 다시 발명하는" 일을 피하기 위해, JCS는 버전 6부터 ECMAScript(일명 JavaScript) [ECMA-262]가 정의한 JSON 기본값 (문자열, 숫자, 리터럴)의 직렬화에 의존한다.¶
숙련된 XML 개발자는 XML 서명을 검증하는 데 어려움을 겪었던 일을 기억할 수 있다. 이는 보통 매우 복잡한 XML 정규화 규칙과 마찬가지로 복잡한 Web Services 보안 표준에 대한 서로 다른 해석 때문이었다. JCS가 유사한 문제를 겪지 않아야 하는 이유는 다음과 같다.¶
JCS는 JSON Web Key(JWK) Thumbprint [RFC7638] 및 Keybase [KEYBASE]처럼 JSON 정규화에 의존하는 일부 기존 시스템과 호환된다.¶
암호화 외의 잠재적 사용에 대해서는 [JSONCOMP]를 참조하라.¶
이 문서의 의도된 독자는 JSON 도구 공급업체와 JSON 기반 암호화 솔루션 설계자이다. 독자는 "JSON" 객체를 포함한 ECMAScript에 대한 지식이 있다고 가정한다.¶
이 문서는 IETF 표준 트랙에 있지 않다는 점에 유의하라. 그러나 적합한 구현은 보안 및 상호운용성 이유로 지정된 동작을 준수해야 한다. 이 텍스트는 필요한 동작을 설명하기 위해 BCP 14를 사용한다.¶
이 문서의 핵심 단어 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", "OPTIONAL"은 여기에 표시된 것처럼 모두 대문자로 나타날 때, 그리고 오직 그럴 때에만 BCP 14 [RFC2119] [RFC8174]에 설명된 대로 해석되어야 한다.¶
이 섹션은 정규 JSON 표현 생성과 관련된 세부 사항 및 JCS가 이를 어떻게 다루는지를 설명한다.¶
부록 F는 기존 JSON 도구에 JCS 지원을 추가하는 RECOMMENDED 방식을 설명한다.¶
정규 직렬화될 데이터는 보통 다음 방식으로 생성된다.¶
사용된 방법과 관계없이, 직렬화될 데이터는 I-JSON [RFC7493] 형식에 맞게 조정되어야 하며(MUST), 이는 다음을 의미한다.¶
추가 제약은 파싱된 JSON 문자열 데이터가 이후 직렬화 중에 변경되어서는 안 된다는 것이다(MUST NOT). 자세한 정보는 부록 E를 참조하라.¶
참고: 유니코드 표준은 특정 문자 시퀀스를 재배열할 수 있는 가능성을 제공하며, 이를 "Unicode Normalization" [UCNORM]이라고 부르지만, JCS 준수 문자열 처리는 이를 고려하지 않는다. 즉, JCS에 의존하는 방식에 관여하는 모든 구성요소는 Unicode 문자열 데이터를 "있는 그대로" 보존해야 한다(MUST).¶
다음 하위 섹션들은 이전 섹션에서 자세히 다룬 데이터의 정규 JSON 표현을 만들기 위해 필요한 단계를 설명한다.¶
부록 A는 JCS 명세와 일치하는 ECMAScript 기반 정규화기의 샘플 코드를 보여준다.¶
다음 JSON 객체가 파싱된다고 가정하라.¶
{
"numbers": [333333333.33333329, 1E30, 4.50,
2e-3, 0.000000000000000000000000001],
"string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/",
"literals": [null, true, false]
}
¶
파싱된 데이터가 이후 ECMAScript의 "JSON.stringify()"를 준수하는 직렬화기를 사용하여 직렬화되면, 결과는 (표시 목적으로만 줄바꿈을 추가하면) 원래 데이터와 상당히 달라진다.¶
{"numbers":[333333333.3333333,1e+30,4.5,0.002,1e-27],"string":
"€$\u000f\nA'B\"\\\\\"/","literals":[null,true,false]}
¶
파싱된 데이터와 직렬화된 대응물 사이의 차이는, 입력 데이터는(JSON [RFC8259]에 정의된 대로) 넓은 허용 범위를 가지는 반면 출력 데이터는(ECMAScript가 정의한 대로) 고정된 표현을 가지기 때문이다. 예제에서 볼 수 있듯이 숫자도 반올림의 대상이 된다.¶
다음 하위 섹션들은 JCS에 따른 기본 JSON 데이터 타입의 직렬화를 설명한다. 이 부분은 ECMAScript의 해당 부분과 동일하다. (가능성은 낮지만) ECMAScript의 향후 버전이 다음 직렬화 방법 중 어느 하나를 무효화하는 경우, 이 명세를 고수할지 새 명세를 만들지는 개발자 커뮤니티에 달려 있다.¶
JSON [RFC8259]에 따라, 리터럴 "null", "true", "false"는 각각 null, true, false로 직렬화되어야 한다(MUST).¶
JSON 문자열 데이터(JSON 객체 속성 이름도 포함)의 경우, 각 Unicode 코드 포인트는 아래에 설명된 대로 직렬화되어야 한다(MUST). [ECMA-262]의 Section 24.3.2.2를 참조하라.¶
마지막으로, 결과 Unicode 코드 포인트 시퀀스는 큰따옴표(")로 둘러싸여야 한다(MUST).¶
참고: "lone surrogates"(예: U+DEAD)와 같은 유효하지 않은 Unicode 데이터는 깨진 서명을 포함한 상호운용성 문제를 초래할 수 있으므로, 이러한 데이터가 나타나면 JCS 준수 구현은 적절한 오류와 함께 종료해야 한다(MUST).¶
ECMAScript는 JSON 숫자 데이터를 표현하기 위해 IEEE 754 [IEEE754] 배정밀도 표준을 기반으로 한다. 이러한 데이터는 "Note 2" 개선사항을 포함하여 [ECMA-262]의 Section 7.1.12.1에 따라 직렬화되어야 한다(MUST).¶
이 부분의 상대적 복잡성 때문에 알고리즘 자체는 이 문서에 포함되지 않는다. JCS 준수 숫자 직렬화 구현자에게는 V8 [V8]의 Google 구현이 참고가 될 수 있다. 또 다른 호환 숫자 직렬화 참조 구현은 Ryu [RYU]이며, 부록 G에 언급된 JCS 오픈 소스 Java 구현에서 사용된다. 부록 B에는 IEEE 754 샘플 값과 그에 해당하는 JSON 직렬화 집합이 있다.¶
참고: Not a Number(NaN) 및 Infinity는 JSON에서 허용되지 않으므로, NaN 또는 Infinity가 나타나면 JCS 준수 구현은 적절한 오류와 함께 종료해야 한다(MUST).¶
이전 단계가 기본 JSON 데이터 타입의 표현을 정규화했지만, JSON 객체 속성이 사전식(알파벳) 순서가 아니므로 결과는 아직 "정규"로 인정되지 않는다.¶
Section 3.2.2의 샘플에 적용하면, 적절히 정규화된 버전은 (표시 목적으로만 줄바꿈을 추가하면) 다음과 같아야 한다.¶
{"literals":[null,true,false],"numbers":[333333333.3333333,
1e+30,4.5,0.002,1e-27],"string":"€$\u000f\nA'B\"\\\\\"/"}
¶
JCS에 따른 JSON 객체 속성의 사전식 정렬 규칙은 다음과 같다.¶
JSON 객체의 속성을 정렬하려고 할 때 다음 조치를 준수해야 한다 (MUST).¶
속성 이름 문자열은 두 문자열 모두에 유효한 어떤 인덱스에서 서로 다른 값을 가지거나, 길이가 다르거나, 또는 둘 다이다. 하나 이상의 인덱스 위치에서 값이 다르면, k를 그러한 가장 작은 인덱스라고 하자. 그러면 위치 k의 값이 더 작은 문자열이 "<" 연산자를 사용하여 결정된 대로 다른 문자열보다 사전식으로 앞선다. 서로 다른 인덱스 위치가 없으면, 더 짧은 문자열이 더 긴 문자열보다 사전식으로 앞선다.¶
평이한 영어로 말하면, 이는 속성 이름이 다음과 같은 오름차순으로 정렬됨을 의미한다.¶
""
"a"
"aa"
"ab"
¶
정렬 알고리즘을 UTF-16 코드 단위에 기반시키는 이유는 이것이 ECMAScript(웹 브라우저와 Node.js에서 제공됨), Java, .NET의 문자열 타입에 직접 매핑되기 때문이다. 또한 JSON은 UTF-16 코드 단위로 표현된 이스케이프 시퀀스만 지원하므로, 이러한 데이터에 대한 지식과 처리는 어차피 필요하다. 다른 내부 문자열 데이터 표현을 사용하는 시스템은 정렬하기 전에 JSON 속성 이름 문자열을 UTF-16 코드 단위 배열로 변환해야 한다. UTF-8 또는 UTF-32에서 UTF-16으로의 변환은 Unicode [UNICODE] 표준에 의해 정의된다.¶
다음 JSON 테스트 데이터는 JCS 구현에서 정렬 방식의 정확성을 검증하는 데 사용할 수 있다.¶
{
"\u20ac": "Euro Sign",
"\r": "Carriage Return",
"\ufb33": "Hebrew Letter Dalet With Dagesh",
"1": "One",
"\ud83d\ude00": "Emoji: Grinning Face",
"\u0080": "Control",
"\u00f6": "Latin Small Letter O With Diaeresis"
}
¶
속성 문자열 정렬 후 예상되는 인수 순서:¶
"Carriage Return" "One" "Control" "Latin Small Letter O With Diaeresis" "Euro Sign" "Emoji: Grinning Face" "Hebrew Letter Dalet With Dagesh"¶
참고: 결정적 속성 순서를 얻기 위한 목적으로는 UTF-8 또는 UTF-32로 인코딩된 데이터를 정렬해도 동작하지만, 위와 같은 JSON 데이터의 결과는 달라지므로 이 명세와 호환되지 않는다. 그러나 실제로 속성 이름은 7비트 ASCII 밖에서 정의되는 일이 드물기 때문에, UTF-16으로 변환하지 않고 UTF-8 또는 UTF-32 형식의 문자열 데이터를 정렬하면서도 JCS와 호환될 수 있다. 이것이 실행 가능한 선택지인지 여부는 JCS가 사용되는 환경에 따라 달라진다.¶
마지막으로, 플랫폼 독립적 표현을 만들기 위해 앞 단계의 결과는 UTF-8로 인코딩되어야 한다(MUST).¶
Section 3.2.3의 샘플에 적용하면, 여기서는 16진수 표기로 표시된 다음 바이트가 생성되어야 한다.¶
7b 22 6c 69 74 65 72 61 6c 73 22 3a 5b 6e 75 6c 6c 2c 74 72 75 65 2c 66 61 6c 73 65 5d 2c 22 6e 75 6d 62 65 72 73 22 3a 5b 33 33 33 33 33 33 33 33 33 2e 33 33 33 33 33 33 33 2c 31 65 2b 33 30 2c 34 2e 35 2c 30 2e 30 30 32 2c 31 65 2d 32 37 5d 2c 22 73 74 72 69 6e 67 22 3a 22 e2 82 ac 24 5c 75 30 30 30 66 5c 6e 41 27 42 5c 22 5c 5c 5c 5c 5c 22 2f 22 7d¶
이 데이터는 암호화 방법의 입력으로 사용할 수 있도록 의도되어 있다.¶
시스템의 무결성에 영향을 줄 수 있는 버퍼 오버플로우와 유사한 문제를 피하기 위해 입력 데이터에 대한 정상성 검사를 수행하는 것이 중요하다.¶
JCS가 부록 F에 설명된 것과 같은 서명 방식에 적용될 때, 애플리케이션은 수신된 데이터에 따라 동작하기 전에 다음 작업을 수행해야 한다(MUST).¶
이러한 단계 중 하나라도 실패하면 진행 중인 작업은 중단되어야 한다(MUST).¶
아래는 ECMAScript 기반 시스템에서 사용하기 위한 JCS 정규화기의 예제이다.¶
////////////////////////////////////////////////////////////
// Since the primary purpose of this code is highlighting //
// the core of the JCS algorithm, error handling and //
// UTF-8 generation were not implemented. //
////////////////////////////////////////////////////////////
var canonicalize = function(object) {
var buffer = '';
serialize(object);
return buffer;
function serialize(object) {
if (object === null || typeof object !== 'object' ||
object.toJSON != null) {
/////////////////////////////////////////////////
// Primitive type or toJSON, use "JSON" //
/////////////////////////////////////////////////
buffer += JSON.stringify(object);
} else if (Array.isArray(object)) {
/////////////////////////////////////////////////
// Array - Maintain element order //
/////////////////////////////////////////////////
buffer += '[';
let next = false;
object.forEach((element) => {
if (next) {
buffer += ',';
}
next = true;
/////////////////////////////////////////
// Array element - Recursive expansion //
/////////////////////////////////////////
serialize(element);
});
buffer += ']';
} else {
/////////////////////////////////////////////////
// Object - Sort properties before serializing //
/////////////////////////////////////////////////
buffer += '{';
let next = false;
Object.keys(object).sort().forEach((property) => {
if (next) {
buffer += ',';
}
next = true;
/////////////////////////////////////////////
// Property names are strings, use "JSON" //
/////////////////////////////////////////////
buffer += JSON.stringify(property);
buffer += ':';
//////////////////////////////////////////
// Property value - Recursive expansion //
//////////////////////////////////////////
serialize(object[property]);
});
buffer += '}';
}
}
};
¶
다음 표는 일부 경계 사례를 포함하여 ECMAScript 호환 숫자 직렬화 샘플 집합을 담고 있다. "IEEE 754" 열은 "Number" 데이터 타입의 내부 ECMAScript 표현을 가리키며, 이는 64비트(배정밀도) 값을 사용하는 IEEE 754 [IEEE754] 표준에 기반하고, 여기서는 16진수로 표현되어 있다.¶
| IEEE 754 | JSON 표현 | 설명 |
|---|---|---|
0000000000000000
|
0
|
Zero
|
8000000000000000
|
0
|
Minus zero
|
0000000000000001
|
5e-324
|
Min pos number
|
8000000000000001
|
-5e-324
|
Min neg number
|
7fefffffffffffff
|
1.7976931348623157e+308
|
Max pos number
|
ffefffffffffffff
|
-1.7976931348623157e+308
|
Max neg number
|
4340000000000000
|
9007199254740992
|
Max pos
int (1)
|
c340000000000000
|
-9007199254740992
|
Max neg
int (1)
|
4430000000000000
|
295147905179352830000
|
~2**68 (2)
|
7fffffffffffffff
|
NaN (3)
|
|
7ff0000000000000
|
Infinity (3)
|
|
44b52d02c7e14af5
|
9.999999999999997e+22
|
|
44b52d02c7e14af6
|
1e+23
|
|
44b52d02c7e14af7
|
1.0000000000000001e+23
|
|
444b1ae4d6e2ef4e
|
999999999999999700000
|
|
444b1ae4d6e2ef4f
|
999999999999999900000
|
|
444b1ae4d6e2ef50
|
1e+21
|
|
3eb0c6f7a0b5ed8c
|
9.999999999999997e-7
|
|
3eb0c6f7a0b5ed8d
|
0.000001
|
|
41b3de4355555553
|
333333333.3333332
|
|
41b3de4355555554
|
333333333.33333325
|
|
41b3de4355555555
|
333333333.3333333
|
|
41b3de4355555556
|
333333333.3333334
|
|
41b3de4355555557
|
333333333.33333343
|
|
becbf647612f3696
|
-0.0000033333333333333333
|
|
43143ff3c1cb0959
|
1424953923781206.2
|
Round to even (4)
|
참고:¶
JCS 숫자 직렬화기의 더 포괄적인 검증을 위해, 개발 포털 (부록 I 참조)에서 (현재) 제공되는 대량의 샘플 값 파일에 대해 테스트할 수 있다. 또 다른 옵션은 상당량의 무작위 IEEE 754 값을 생성하는 프로그램과 함께 V8 [V8]을 실시간 참조로 실행하는 것이다.¶
정규화 프로세스의 결과(Section 3.2.4 참조)는 완전히 유효한 JSON이므로 "Wire Format"으로도 사용할 수 있다. 그러나 대부분의 경우 JCS에 기반한 암호화 방식은 외부에서 제공된 JSON 데이터가 이미 정규화되어 있는지에 의존하지 않으므로, 이는 단지 선택사항일 뿐이다.¶
실제로 "JSON.stringify()"를 사용해 객체를 직렬화하는 ECMAScript 표준 방식은 속성이 생성되거나 수신된 순서로 유지되는 더 "논리적인" 형식을 생성한다. 아래 예제는 ECMAScript 표준 직렬화의 이점을 얻을 수 있는 주소 레코드를 보여준다.¶
{
"name": "John Doe",
"address": "2000 Sunset Boulevard",
"city": "Los Angeles",
"zip": "90001",
"state": "CA"
}
¶
정규화를 사용하면 위 속성은 "address", "city", "name", "state", "zip" 순서로 출력되며, 이는 사람(개발자 또는 기술 지원)의 관점에서 데이터에 모호함을 더한다. 정규화는 또한 JSON 데이터를 한 줄의 텍스트로 변환하므로, 디버깅과 로깅에는 덜 이상적일 수 있다.¶
JSON 숫자 타입과 관련된 여러 문제가 있으며, 여기서는 다음 샘플 객체로 이를 설명한다.¶
{
"giantNumber": 1.4e+9999,
"payMeThis": 26000.33,
"int64Max": 9223372036854775807
}
¶
위 샘플이 JSON [RFC8259]을 준수하더라도, 애플리케이션은 보통 "giantNumber"와 "int64Max"를 저장하기 위해 서로 다른 네이티브 데이터 타입을 사용한다. 또한 "payMeThis"와 같은 금전 데이터는 십진 산술과 관련된 반올림 문제 때문에 부동소수점 데이터 타입에 의존하지 않을 것으로 추정된다.¶
JSON 숫자 타입의 이러한 종류의 "오버로딩"을 처리하는 확립된 방식(적어도 확장 가능한 방식)은 매핑 메커니즘을 통하는 것이며, 이는 파서가 속성 이름을 기반으로 각기 다른 속성에 대해 무엇을 해야 하는지 지시한다. 그러나 이는 원래의 다소 제한된 JavaScript 맥락 밖에서 JSON 숫자 타입을 사용하는 가치를 크게 제한한다. ECMAScript "JSON" 객체도 JSON 숫자 타입에 대한 매핑을 지원하지 않는다.¶
위와 같은 이유로, 현재 JSON 생태계에서 자연스러운 위치를 갖지 않는 숫자는 JSON 문자열 타입을 사용하여 감싸야 한다 (MUST). 이는 개방형 시스템에서 사실상의 표준에 가깝다. 이는 부록 E에 설명된 "DateTime" 객체처럼 JSON에서 직접 지원하지 않는 다른 데이터 타입에도 적용된다.¶
JSON 문자열 타입을 사용하는 시스템의 도움을 받으면, 예를 들어 다음과 같은 프로그래밍 방식이든¶
var obj = JSON.parse('{"giantNumber": "1.4e+9999"}');
var biggie = new BigNumber(obj.giantNumber);
¶
OpenAPI [OPENAPI]와 같은 선언적 방식이든, JCS는 ECMAScript를 사용할 때를 포함하여 애플리케이션에 제한을 부과하지 않는다.¶
JSON에서 제공되는 데이터 타입 집합이 제한되어 있기 때문에, JSON 문자열 타입은 일반적으로 하위 타입을 담는 데 사용된다. 이는 JSON 파싱 방법에 따라 상호운용성 문제로 이어질 수 있으며, 더 넓은 독자를 대상으로 하는 JCS 준수 애플리케이션은 이를 처리해야 한다(MUST).¶
스키마 설계자가 "big" 속성을 "BigInt" 하위 타입을 담도록, "time" 속성을 "DateTime" 하위 타입을 담도록 할당했으며, "val"은 JCS를 준수하는 JSON 숫자라고 가정한 JSON 객체를 파싱하려 한다고 하자. 다음 예제는 그러한 객체를 보여준다.¶
{
"time": "2019-01-28T07:45:10Z",
"big": "055",
"val": 3.5
}
¶
이 객체의 파싱은 다음 ECMAScript 문장으로 수행할 수 있다.¶
var object = JSON.parse(JSON_object_featured_as_a_string);¶
파싱 후 실제 데이터를 추출할 수 있으며, 하위 타입의 경우에는 파싱 프로세스의 결과(ECMAScript 객체)를 입력으로 사용하는 변환 단계도 포함된다.¶
... = new Date(object.time); // Date object ... = BigInt(object.big); // Big integer ... = object.val; // JSON/JS number¶
"BigInt" 데이터 타입은 현재 V8 [V8]에서만 네이티브로 지원된다는 점에 유의하라.¶
부록 A의 샘플 코드를 사용하여 "object"를 정규화하면 다음 문자열이 반환된다.¶
{"big":"055","time":"2019-01-28T07:45:10Z","val":3.5}
¶
이것은 (JCS와 관련해서는) 기술적으로 올바르지만, JSON 데이터를 파싱하는 또 다른 방법이 있으며, 이는 아래에 보인 것처럼 ECMAScript와 함께 사용할 수도 있다.¶
// "BigInt" requires the following code to become JSON serializable
BigInt.prototype.toJSON = function() {
return this.toString();
};
// JSON parsing using a "stream"-based method
var object = JSON.parse(JSON_object_featured_as_a_string,
(k,v) => k == 'time' ? new Date(v) : k == 'big' ? BigInt(v) : v
);
¶
이제 부록 A의 정규화기를 "object"에 적용하면 다음 문자열이 생성된다.¶
{"big":"55","time":"2019-01-28T07:45:10.000Z","val":3.5}
¶
이 경우 "big" 및 "time"의 문자열 인수가 원본과 비교해 변경되어, JCS에 의존하는 애플리케이션이 실패하게 만들 것으로 추정된다.¶
이러한 차이가 발생하는 이유는 스트림 기반 및 스키마 기반 JSON 파서에서 원래 문자열 인수가 보통 즉석에서 네이티브 하위 타입으로 대체되며, 직렬화될 때 다른 플랫폼 의존적 패턴을 보일 수 있기 때문이다.¶
즉, 스트림 기반 및 스키마 기반 파싱은 하위 타입을 "순수한" (불변) JSON 문자열 타입으로 취급해야 하며(MUST), 지정된 네이티브 타입으로의 실제 변환은 이후 단계에서 수행해야 한다. Go, Java, C#과 같은 현대 프로그래밍 플랫폼에서는 어노테이션, getter, setter를 결합하여 적당한 노력으로 이를 달성할 수 있다. 아래는 JSON 객체로 직렬화 가능한 클래스 일부를 보여주는 C#/Json.NET 예제이다.¶
// The "pure" string solution uses a local
// string variable for JSON serialization while
// exposing another type to the application
[JsonProperty("amount")]
private string _amount;
[JsonIgnore]
public decimal Amount {
get { return decimal.Parse(_amount); }
set { _amount = value.ToString(); }
}
¶
애플리케이션에서 "Amount"는 다른 속성과 마찬가지로 접근할 수 있지만, JSON 컨텍스트에서는 실제로 따옴표로 묶인 문자열로 표현된다.¶
참고: 위 예제는 I-JSON이 암시하는 숫자 데이터 제약도 다룬다 (C#의 "decimal" 데이터 타입은 IEEE 754 배정밀도와 비교해 상당히 다른 특성을 가진다).¶
최적의 해결책은 JCS 지원을 JSON 직렬화기에 직접 통합하는 것이다(파서는 변경할 필요가 없다). 즉, 정규화는 JSON 직렬화기의 추가 "모드"일 뿐이다. 그러나 현재는 그렇지 않다. 다행히 JCS 지원은 기존 JSON 직렬화기에 대한 후처리기로 동작하는 외부 제공 정규화기 소프트웨어를 통해 도입될 수 있다. 이 구성은 또한 JCS 구현자가 기본 데이터가 JSON에서 어떻게 표현되어야 하는지를 다룰 필요를 덜어준다.¶
후처리기 개념은 다음과 같은 서명 생성 방식을 가능하게 한다.¶
호환되는 서명 검증 방식은 다음과 같다.¶
위와 같은 정규화기는 사실상 "필터"일 뿐이며, 매우 다양한 암호화 방식과 함께 사용할 수 있을 가능성이 있다.¶
JCS 지원이 통합된 JSON 직렬화기를 사용하면, 정규화 단계 전에 수행되는 직렬화를 두 프로세스 모두에서 제거할 수 있다.¶
다음 오픈 소스 구현은 JCS와 호환되는 것으로 검증되었다.¶
"Canonical JSON"을 만들려는 다른 노력들이 존재하며(또는 존재해 왔으며), 아래는 그중 일부 URL 목록이다.¶
나열된 노력들은 모두 텍스트 수준의 JSON-to-JSON 변환을 기반으로 한다. 텍스트 수준 정규화의 주요 특징은 사용되는 JSON의 종류에 대해 중립적으로 만들 수 있다는 것이다. 그러나 이러한 방식은 JSON 파싱 프로세스에 중대한 변경을 암시하므로, 채택의 장애물이 될 가능성이 높다. 특정 JSON 및 애플리케이션 제약을 감수하더라도, JCS는 기존 JSON 도구와 호환되도록 설계되었다.¶
JCS 명세는 현재 다음 위치에서 개발되고 있다. <https://github.com/cyberphone/ietf-json-canon>.¶
JCS 소스 코드와 광범위한 테스트 데이터는 다음 위치에서 제공된다. <https://github.com/cyberphone/json-canonicalization>.¶
ECMAScript 숫자 직렬화를 기반으로 하는 것은 원래 James Manger가 제안했다. 이는 결국 JSON 기본값에 대한 전체 ECMAScript 직렬화 방식의 채택으로 이어졌다.¶
이 명세에 귀중한 의견을 제공한 다른 사람들에는 Scott Ananian, Tim Bray, Ben Campbell, Adrian Farell, Richard Gibson, Bron Gondwana, John-Mark Gurney, Mike Jones, John Levine, Mark Miller, Matthew Miller, Mark Nottingham, Mike Samuel, Jim Schaad, Robert Tupelo-Schneck, and Michal Wadas가 포함된다.¶
실제 세계의 개념 검증을 수행하는 데 있어, Ulf Adams, Tanner Gooding, and Remy Oudompheng가 제공한 숫자 직렬화용 소프트웨어와 지원이 매우 도움이 되었다.¶