Microsoft MVP성태의 닷넷 이야기
오류 유형: 468. JSON.parse가 허용하지 않는 문자 [링크 복사], [링크+제목 복사],
조회: 25977
글쓴 사람
정성태 (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
정성태

... 151  152  153  154  155  156  157  158  [159]  160  161  162  163  164  165  ...
NoWriterDateCnt.TitleFile(s)
1073정성태6/20/201127171오류 유형: 127. Visual Studio에서 WCF 서비스의 이름 변경 시 발생할 수 있는 오류
1072정성태6/19/201126644.NET Framework: 224. EF 4.1 Code First에서 Identity 칼럼 생성하는 방법파일 다운로드1
1071정성태6/19/201130201.NET Framework: 223. Entity Framework 4.1의 Code First를 이용한 SQL Azure 데이터베이스 생성 [3]파일 다운로드1
1070정성태6/19/201127711.NET Framework: 222. Windows Azure - VM Role 베타 프로그램 참여 [2]
1069정성태6/18/201127807.NET Framework: 221. Cache 영향을 받지 않는 DNS 이름 풀이 [2]파일 다운로드1
1068정성태6/16/201125393개발 환경 구성: 127. Portable Library - 닷넷 N-Screen용 공통 라이브러리 제작 [1]
1067정성태6/15/201124954오류 유형: 126. Windows failed to apply the Group Policy Folder Options settings. [1]
1066정성태6/14/201127984개발 환경 구성: 126. MSDN 구독자 - Windows Azure 무료 서비스 신청하는 방법 [4]
1065정성태6/13/201132811개발 환경 구성: 125. Firebird - 유니코드 기본 문자셋 지정
1064정성태6/11/201127459웹: 22. Visual Studio 2010에서 CSS 3 인텔리센스(intellisense) 지원하는 방법 [1]
1063정성태6/10/201129054웹: 21. Sysnet 웹 사이트의 CSS 2.1 변환 기록 [1]
1062정성태6/9/201129210웹: 20. Sysnet 웹 사이트의 HTML5 변환 기록 [1]
1061정성태6/8/201127457오류 유형: 125. 인터넷 익스플로러 - 개발자 도구에서 정지점(BP: Breakpoint) 설정이 안 되는 경우 [1]
1060정성태6/8/201124017VC++: 51. PHP 모듈의 F5 디버깅
1059정성태6/6/201129141VC++: 50. PHP 모듈 - php_mysql 빌드하는 방법파일 다운로드1
1058정성태6/5/201132791개발 환경 구성: 124. .NET 개발자가 처음 해보는 PHP + MySQL 연동 [2]
1057정성태6/4/201130170VC++: 49. 소스 코드로부터 php5apache2_2.dll 생성하는 방법파일 다운로드1
1056정성태6/2/201128328VC++: 48. 윈도우에서 Apache Module - Content Handler 컴파일파일 다운로드1
1055정성태6/1/201125543오류 유형: 124. MVC 프로젝트의 Site.Master 관련 오류 정리
1054정성태5/31/201129814.NET Framework: 220. ASP.NET MVC Web Site 프로젝트 - 단위 테스트 작성파일 다운로드1
1053정성태5/31/201132328VC++: 47. Apache Module에 대한 'F5 디버그 (Start with debugging)' [2]
1052정성태5/30/201129956.NET Framework: 219. ASP.NET MVC Web Site 프로젝트 구성하기파일 다운로드1
1051정성태5/28/201138444VC++: 46. 윈도우에서 Apache Module 컴파일 (VC++)파일 다운로드1
1050정성태5/28/201124610오류 유형: 123. Firebird - Exception of type 'FirebirdSql.Data.Common.IscException' was thrown.
1049정성태5/28/201130309.NET Framework: 218. WCF REST 서비스 - 웹 브라우저 측 Ajax 호출 캐시 [1]
1048정성태5/27/201132238개발 환경 구성: 123. Apache 소스를 윈도우 환경에서 빌드하기
... 151  152  153  154  155  156  157  158  [159]  160  161  162  163  164  165  ...