Microsoft MVP성태의 닷넷 이야기
오류 유형: 468. JSON.parse가 허용하지 않는 문자 [링크 복사], [링크+제목 복사],
조회: 18092
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)

JSON.parse가 허용하지 않는 문자

아래의 웹 페이지를 브라우저로 보면,

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>

    <script>
        function parse_input() {
            var txt = '{"firstname":"My\tName"}';
            var obj = JSON.parse(txt);
        }
    </script>

</head>
<body>
    <input type="button" onclick="parse_input()" value="test" />
</body>
</html>

F12 개발자 콘솔을 통해 parse_input 함수 수행 시 다음과 같은 오류가 나는 것을 볼 수 있습니다.

[Internet Explorer 11]

SCRIPT1014: Invalid character
encode_sample.html (15,13)

[Edge]

SCRIPT5655: SCRIPT5655: JSON.parse Error: Invalid character at position:17

[Chrome]

VM32:1 Uncaught SyntaxError: Unexpected token    in JSON at position 18
    at JSON.parse (<anonymous>)
    at parse_input (encode_sample.html:15)
    at HTMLInputElement.onclick (encode_sample.html:22)

원인은 json 텍스트에 포함된 탭(\t) 문자 때문입니다. 따라서 이런 문제를 예방하려면 해당 텍스트들의 입력 시에 미리 json parse로 테스트하든가, 아니면 다음과 같은 식으로 문자열 필터링을 하는 것이 좋습니다.

function check_validInput(txt) {
    var replacedText = txt.replace(/[\u0000-\u0019]+/g, " "); // https://stackoverflow.com/questions/14432165/uncaught-syntaxerror-unexpected-token-with-json-parse/41498991
    if (txt != replacedText) {
        alert("INVALID");
        return false;
    }

    alert("VALID");
    return true;
}




재미있는 것은, 이러한 탭 문자 등의 것들이 본래 JSON 표준의 제약은 아니라는 점입니다. 일례로, C#의 JSON.NET으로 실행하면 잘 파싱이 됩니다.

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            string txt = "{ \"firstname\": \"test\t\tqwer\" }";
            object result = Newtonsoft.Json.JsonConvert.DeserializeObject(txt);
            Console.WriteLine(result);
        }
    }
}

/*
출력 결과:

{
  "firstname": "test\t\tqwer"
}
*/

그런데 3개의 브라우저(IE, Edge, Chrome) 모두 마치 서로 같은 JSON parser 코드를 공유하기라도 한 것처럼 오류가 발생한다는 것이 매우 흥미롭습니다. 사실, JSON 포맷 표준에서는,

JSON 데이터 교환 포맷
; https://techhtml.github.io/ECMA-404/

ECMA-404
; http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf

문자열의 범위로,

String: a sequence of zero or more Unicode characters. Strings are delimited with double-quotation marks and support a backslash escaping syntax.

분명히 '\'를 이용한 이스케이프 문자를 지원한다고 명시합니다. 또한, 다음의 표준 문서를 봐도,

Standard ECMA-262
5.1 Edition / June 2011
ECMAScript® Language Specification
; https://www.ecma-international.org/ecma-262/5.1/#sec-15.12.2

JSON 구문의 가능한 값으로 "JSONString"이라고 명시하고 있으며 또한 stringify의 "NOTE 3"에 다음과 같은 문구가 나옵니다.

String values are wrapped in double quotes. The characters " and \ are escaped with \ prefixes. Control characters are replaced with escape sequences \uHHHH, or with the shorter forms, \b (backspace), \f (formfeed), \n (newline), \r (carriage return), \t (tab).

혹시 왜? 브라우저의 JSON.parse 메서드가 \b, \f, \n, \r, \t 문자들에 대해 파싱 오류가 발생하는지 정확한 이력을 아시는 분은 덧글 부탁드립니다. ^^




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 6/5/2018]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2018-06-05 12시49분
[spowner] \\t 이런 식으로 표기해야 하는게 아닐까요? json문서는 \ 문자들을 처리한다고 이해됩니다...
[guest]
2018-06-05 01시52분
아래의 문장을 두고 의견을 주신 것 같은데,

"The characters " and \ are escaped with \ prefixes."

위의 것은 겹따옴표와 역슬래시 문자를 표현하고 싶으면 그에 대해 역슬래시 문자를 하나 더 추가해야 한다는 것입니다. 그 외의 것들은 모두 역슬래시가 하나로 되어 있는 것이 맞습니다.

\\t 이런 식으로 표기하면 JSON에게는 앞의 역슬래시 2개가 '\' 문자로 해석되어 결국 '\', 't' 2글자로 인식됩니다. 즉 탭 문자로 인식하지 않습니다.
정성태
2018-06-05 02시13분
[spowner] var txt = '{"firstname":"My\\tName"}';
에서 이미 \t는 탭으로 해석이 되고
탭은 JSON.parse에서 오류를 발생합니다.
\" 을 포함해서 JSON.parse에 역슬래쉬를 넣어줘야 합니다.
즉, 탭은
var txt = '{"firstname":"My\\tName"}';
으로 JSON.parse에 "\t"으로('\', 't' 두글자로) 넘겨줘야 합니다.
문서로 봤을 때 저는 그렇게 이해했습니다
[guest]
2018-06-05 02시16분
[spowner] 위의 댓글 첫번째 코드가 역슬래쉬 두개로 들어갔는지 모르겠네요. 하나입니다; JSON String 정의에는 U+0000부터 U+001F의 컨트롤 문자는 직접 표현할 수 없습니다. 탭의 경우 이 범위안에 들어가 직접 표현이 안되고 앞에 역슬래쉬를 붙어야 합니다
[guest]
2018-06-05 02시57분
방금 전 덧글은 테스트 안 하고 쓴 글인데, ^^; 테스트를 해보니 말씀해 주신 내용이 맞습니다. 게다가 JSON.NET으로 테스트를 해보니, '\t'뿐만 아니라 '\\t'도 정상적으로 탭 문자로 변환되는 것을 확인했습니다. 그렇게 보면 오히려 JSON.NET이 '\t'에 대한 처리를 부가적으로 (편의상) 해주는 것 같습니다.

결국, JSON 텍스트를 주고 받을 때 모든 문자열에 대해 JSON parser에 전달하려면 탭 문자를 (포함해 몇몇 이스케이프 문자를) "\t"에서 "\\t"와 같은 식으로 바꿔야 한다는 불편함이 발생합니다.

실제로 Javascript의 문자열은 '\t'를 탭 문자로 인식합니다.

  var txt = '{"firstname":"My\tName"}';
  alert("txt: " + txt); // My Name

물론 HTTPRequest 등을 통해 서버로부터 전송받은 문자열의 탭 문자도 저런 식으로 옵니다.

따라서 \t (뿐만 아니라 \n 등의 몇몇 이스케이프 문자)를 포함한 문자열을 \\t와 같은 식으로 반드시 전처리를 해줘야 하고,

  var txt = '{"firstname":"My\tName"}';
  var replacedText = txt.replace(/\t/g, "\\t")
                        .replace(/\n/g, "\\n");
                
  var obj = JSON.parse(replacedText);

이어서 JSON.stringify로 반환한 문자열도 마찬가지로 \\t를 다시 \t로 바꿔주는 후처리를 해줘야 하는 식이 됩니다. 그럼 이제 JSON이 왜 이런 식으로 불편한 처리를 유도했는지에 대해 궁금해지는군요. ^^
정성태
2018-06-05 03시08분
생각해 보니, 완전히 문자열로 표현 가능하게 만든다는 취지에서 봤을 때 \\t로 하는 것이 일관성이 있는 것 같습니다. 전/후처리가 필요하다는 단점은 있지만.
정성태
2018-06-05 03시22분
[spowner] 정성태님의 댓글에 동의합니다. 정의라는게 관점에 따라 다르긴 해서... 스펙이란게 그런것을 강제하는 효과도 있는 것 같아요
[guest]
2018-06-05 03시39분
그나저나, spowner님 같은 분들의 덧글 덕분에 블로그하는 맛이 납니다. ^^ 이렇게 글을 안 썼다면 모르고 지나갔을 테니.
정성태
2018-06-05 12시12분
댓글의 내용을 반영해 다음의 글로 재작성했습니다.

JSON의 escape sequence 문자 처리 방식
; http://www.sysnet.pe.kr/2/0/11532
정성태

... 16  17  18  19  20  21  22  23  24  25  26  27  [28]  29  30  ...
NoWriterDateCnt.TitleFile(s)
12931정성태1/20/20226412개발 환경 구성: 632. ASP.NET Core 프로젝트를 AKS/k8s에 올리는 과정
12930정성태1/19/20227054개발 환경 구성: 631. AKS/k8s의 Volume에 파일 복사하는 방법
12929정성태1/19/20226855개발 환경 구성: 630. AKS/k8s의 Pod에 Volume 연결하는 방법
12928정성태1/18/20227000개발 환경 구성: 629. AKS/Kubernetes에서 호스팅 중인 pod에 shell(/bin/bash)로 진입하는 방법
12927정성태1/18/20226756개발 환경 구성: 628. AKS 환경에 응용 프로그램 배포 방법
12926정성태1/17/20227247오류 유형: 787. AKS - pod 배포 시 ErrImagePull/ImagePullBackOff 오류
12925정성태1/17/20227336개발 환경 구성: 627. AKS의 준비 단계 - ACR(Azure Container Registry)에 docker 이미지 배포
12924정성태1/15/20228825.NET Framework: 1134. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 비디오 디코딩 예제(decode_video.c) [2]파일 다운로드1
12923정성태1/15/20227754개발 환경 구성: 626. ffmpeg.exe를 사용해 비디오 파일을 MPEG1 포맷으로 변경하는 방법
12922정성태1/14/20226807개발 환경 구성: 625. AKS - Azure Kubernetes Service 생성 및 SLO/SLA 변경 방법
12921정성태1/14/20225778개발 환경 구성: 624. Docker Desktop에서 별도 서버에 설치한 docker registry에 이미지 올리는 방법
12920정성태1/14/20226538오류 유형: 786. Camtasia - An error occurred with the camera: Failed to Add Video Sampler.
12919정성태1/13/20226350Windows: 199. Host Network Service (HNS)에 의해서 점유되는 포트
12918정성태1/13/20226591Linux: 47. WSL - shell script에서 설정한 환경 변수가 스크립트 실행 후 반영되지 않는 문제
12917정성태1/12/20225799오류 유형: 785. C# - The type or namespace name '...' could not be found (are you missing a using directive or an assembly reference?)
12916정성태1/12/20225529오류 유형: 784. TFS - One or more source control bindings for this solution are not valid and are listed below.
12915정성태1/11/20225807오류 유형: 783. Visual Studio - We didn't find any interpreters
12914정성태1/11/20227765VS.NET IDE: 172. 비주얼 스튜디오 2022의 파이선 개발 환경 지원
12913정성태1/11/20228281.NET Framework: 1133. C# - byte * (바이트 포인터)를 FileStream으로 쓰는 방법 [1]
12912정성태1/11/20228913개발 환경 구성: 623. ffmpeg.exe를 사용해 비디오 파일의 이미지를 PGM(Portable Gray Map) 파일 포맷으로 출력하는 방법 [1]
12911정성태1/11/20226223VS.NET IDE: 171. 비주얼 스튜디오 - 더 이상 만들 수 없는 "ASP.NET Core 3.1 Web Application (.NET Framework)" 프로젝트
12910정성태1/10/20226717제니퍼 .NET: 30. 제니퍼 닷넷 적용 사례 (8) - CPU high와 DB 쿼리 성능에 문제가 함께 있는 사이트
12909정성태1/10/20228104오류 유형: 782. Visual Studio 2022 설치 시 "Couldn't install Microsoft.VisualCpp.Redist.14.Latest"
12908정성태1/10/20225951.NET Framework: 1132. C# - ref/out 매개변수의 IL 코드 처리
12907정성태1/9/20226411오류 유형: 781. (youtube-dl.exe) 실행 시 "This app can't run on your PC" / "Access is denied." 오류 발생
12906정성태1/9/20227052.NET Framework: 1131. C# - 네임스페이스까지 동일한 타입을 2개의 DLL에서 제공하는 경우 충돌을 우회하는 방법 [1]파일 다운로드1
... 16  17  18  19  20  21  22  23  24  25  26  27  [28]  29  30  ...