Microsoft MVP성태의 닷넷 이야기
.NET Framework: 351. JavaScriptSerializer, DataContractJsonSerializer, Json.NET [링크 복사], [링크+제목 복사],
조회: 25849
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

(시리즈 글이 9개 있습니다.)
.NET Framework: 351. JavaScriptSerializer, DataContractJsonSerializer, Json.NET
; https://www.sysnet.pe.kr/2/0/1391

.NET Framework: 661. Json.NET의 DeserializeObject 수행 시 속성 이름을 동적으로 바꾸는 방법
; https://www.sysnet.pe.kr/2/0/11224

.NET Framework: 756. JSON의 escape sequence 문자 처리 방식
; https://www.sysnet.pe.kr/2/0/11532

사물인터넷: 54. 아두이노 환경에서의 JSON 파서(ArduinoJson) 사용법
; https://www.sysnet.pe.kr/2/0/11766

.NET Framework: 1073. C# - JSON 역/직렬화 시 리플렉션 손실을 없애는 JsonSrcGen
; https://www.sysnet.pe.kr/2/0/12688

.NET Framework: 2087. .NET 6부터 SourceGenerator와 통합된 System.Text.Json
; https://www.sysnet.pe.kr/2/0/13214

.NET Framework: 2115. System.Text.Json의 역직렬화 시 필드/속성 주의
; https://www.sysnet.pe.kr/2/0/13342

닷넷: 2261. C# - 구글 OAuth의 JWT (JSON Web Tokens) 해석
; https://www.sysnet.pe.kr/2/0/13623

닷넷: 2265. C# - System.Text.Json의 기본적인 (한글 등에서의) escape 처리
; https://www.sysnet.pe.kr/2/0/13644




JavaScriptSerializer, DataContractJsonSerializer, Json.NET


닷넷에는 기본적으로 JavaScriptSerializer, DataContractJsonSerializer 타입이 JSON 직렬화와 관련되어 제공됩니다. 각각 장단점이 있는데요. JavaScriptSerializer는 부가적인 작업 없이 모든 객체의 값을 Key/Value의 쌍으로 직렬화/역직렬화할 수 있습니다. 즉, strong-type이 제공되지 않습니다. 이를 보완하기 위해 클래스를 이용한 strong-type을 지원하는 DataContractJsonSerializer를 사용할 수 있는데, 당연히 클래스 구조는 그에 맞게 만들어 주어야 합니다.

예를 들어, 다음은 페이스북의 me 쿼리의 일부를 표현한 것인데요.

{
"id":"ttt",
"work":[
	{"employer":{"id":"zzz","name":"..."},
	 "start_date":"1992-01",
	}
],
"timezone":9,
"verified":true,
"updated_time":"2012-11-16T07:23:07+0000"
}

DataContractJsonSerializer로 역직렬화하기 위해 다음과 같이 클래스들을 만들어 주고,

public class GraphUser
{
    public string id { get; set; }

    public GraphWork[] work { get; set; }

    public int timezone { get; set; }
    public bool verified { get; set; }
    public string updated_time { get; set; }
}

public class GraphEmployer
{
    public string id { get; set; }
    public string name { get; set; }
}

public class GraphWork
{
    public GraphEmployer employer { get; set; }
    public string start_date { get; set; }
}

이렇게 코딩을 해주면 됩니다.

static void Main(string[] args)
{
    string json = "{ \"id\":\"ttt\", \"work\":[ 	{\"employer\":{\"id\":\"zzz\",\"name\":\"...\"},  \"start_date\":\"1992-01\" } ], \"timezone\":9, \"verified\":true, \"updated_time\":\"2012-11-16T07:23:07+0000\" }";

    DataContractJsonSerializer dcjs = new DataContractJsonSerializer(typeof(GraphUser));
    GraphUser user2 = dcjs.ReadObject(StringToStream(json)) as GraphUser;

    Console.WriteLine(user2.id);
    Console.WriteLine(user2.work[0].employer.id);
}




그런데, 여기서 문제가 있습니다. 만약 string 데이터에 "\n"를 포함하게 되면 DataContractJsonSerializer.ReadObject 호출 시에 예외가 발생합니다. 예를 들어, 위의 json 문자열의 id 값이 다음과 같은 경우입니다.

    string json = "{ \"id\":\"t\ntt\", ...[생략]... }";

그럼, 이런 예외 메시지가 발생합니다.

Unhandled Exception: System.Runtime.Serialization.SerializationException: Therewas an error deserializing the object of type ConsoleApplication1.GraphUser. Encountered invalid character ''. ---> System.FormatException: Encountered invalid character ''.
   at System.Runtime.Serialization.Json.XmlJsonReader.ComputeQuotedTextLengthUntilEndQuote(Byte[] buffer, Int32 offset, Int32 offsetMax, Boolean& escaped)
   at System.Runtime.Serialization.Json.XmlJsonReader.ReadQuotedText(Boolean moveToText)
   at System.Runtime.Serialization.Json.XmlJsonReader.Read()
   at System.Xml.XmlBaseReader.ReadElementContentAsString()
   at System.Runtime.Serialization.XmlReaderDelegator.ReadElementContentAsString()
   ...[생략]...
   at System.Runtime.Serialization.Json.DataContractJsonSerializer.ReadObject(XmlDictionaryReader reader)
   at System.Runtime.Serialization.Json.DataContractJsonSerializer.ReadObject(Stream stream)
   at ConsoleApplication1.Program.Main(String[] args) in d:\...\Program.cs:line 28

문제의 원인은 "\n" 문자에서 역슬래시가 1개가 아닌 2개여야 한다는 점입니다. 그래서, 다음과 같이 변경해 주면 정상적으로 역직렬화를 합니다.

    string json = "{ \"id\":\"t\\ntt\", ...[생략]... }";

// 또는

    string json = "{ \"id\":\"t\\u000att\", ...[생략]... }";

그런데, 이런 경우가 왜 발생할까요? 물론 DataContractJsonSerializer로 직렬화했다면 저런 식으로 문자열이 나오지 않습니다. 제가 경험했던 곳은 바로 페이스북의 /me 쿼리에서 저런 결과를 볼 수 있었습니다. 따라서, 페이스북으로부터 json 문자열을 받는다면 반드시 '\n' 문자를 '\\n'으로 치환해 주는 작업이 선행되어야 합니다.




\n 문자에 대한 처리를 하지 않고 역직렬화를 하고 싶다면 Json.NET을 이용하면 됩니다. (다운로드 및 그에 대한 소스 코드는 모두 공개되어 있습니다.)

Json.NET
; http://json.codeplex.com/

Nuget을 지원하기 때문에 Visual Studio 2012에서 "View" / "Other Windows" / "Package Manager Console" 창을 띄우고, 다음과 같이 입력하면 Json.NET 이 프로젝트에 추가됩니다.

Package Manager Console Host Version 2.1.31002.9028

Type 'get-help NuGet' to see all available NuGet commands.

PM> Install-Package Newtonsoft.Json
Successfully installed 'Newtonsoft.Json 4.5.11'.
Successfully added 'Newtonsoft.Json 4.5.11' to ConsoleApplication1.

PM> 

다음은 Json.NET을 이용한 간단한 코드입니다.

string json = "{ \"id\":\"t\ntt\", \"work\":[ 	{\"employer\":{\"id\":\"zzz\",\"name\":\"...\"},  \"start_date\":\"1992-01\" } ], \"timezone\":9, \"verified\":true, \"updated_time\":\"2012-11-16T07:23:07+0000\" }";

GraphUser user = JsonConvert.DeserializeObject<GraphUser>(json);

Json.NET이 좋은 또 다른 이유가 하나 있습니다. 바로 DateTime 값의 역직렬화입니다. 예제의 json 문자열에 보면 updated_time의 값이 "2012-11-16T07:23:07+0000"인데 이를 그냥 "string" 타입으로 역직렬화하고 있지만, 만약 이 필드의 타입을 DateTime으로 바꾸게 되면,

public class GraphUser
{
    public string id { get; set; }

    public GraphWork[] work { get; set; }

    public int timezone { get; set; }
    public bool verified { get; set; }
    public DateTime updated_time { get; set; }
}

DataContractJsonSerializer으로 역직렬화하는 경우 다음과 같은 예외가 발생합니다.

There was an error deserializing the object of type ConsoleApplication1.GraphUser. DateTime content '2012-11-16T07:23:07+0000' does not start with '\/Date(' and end with ')\/' as required for JSON.


이런 상황에서 표준이냐 아니냐의 문제는 중요하지 않습니다. (그 유명한 페이스북에서 이런 식으로 데이터를 넘겨주기 때문에. ^^;)

하지만, 역시 이를 Json.NET으로 역직렬화하면 잘 됩니다. "2012-11-16T07:23:07+0000" UTC 시간이므로 변환하고 나면 한글 윈도우의 경우 +9 시간대를 적용받아 "{2012-11-16 16:23:07}" 값으로 계산합니다.

결론은, 3가지 Json 직렬화 방법 중에서 가능한 Json.NET을 이용하시는 것이 좋습니다. ^^
(첨부한 파일은 위의 간단한 코드를 담고 있는 테스트 프로젝트입니다.)




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







[최초 등록일: ]
[최종 수정일: 5/9/2024]

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

비밀번호

댓글 작성자
 



2012-12-21 11시20분
[lancers] 성능 문제도 있어요.
JSON.NET이 갑이었던 걸로 기억함...
[guest]
2012-12-22 12시22분
^^ 성능까지 좋았군요. 정보 감사드립니다.
정성태
2012-12-22 01시49분
[lancers] ASP.NET Web API에서 default json serializer를 아예 Json.net올 바꾼다고 했었는데, RTM에서도 유효한지 모르겠네요.
RC에서는 확실히 반영되어 있었습니다.
근데, 요즘 ASP.NET을 쓸 일이 없어서 확인을 못했네요.. ㅋㅋ
[guest]

... 76  77  78  79  80  81  82  83  [84]  85  86  87  88  89  90  ...
NoWriterDateCnt.TitleFile(s)
11836정성태3/5/201923226오류 유형: 525. Visual Studio 2019 Preview 4/RC - C# 8.0 Missing compiler required member 'System.Range..ctor' [1]
11835정성태3/5/201921748.NET Framework: 810. C# 8.0의 Index/Range 연산자를 .NET Framework에서 사용하는 방법 및 비동기 스트림의 컴파일 방법 [3]파일 다운로드1
11834정성태3/4/201920575개발 환경 구성: 432. Visual Studio 없이 최신 C# (8.0) 컴파일러를 사용하는 방법
11833정성태3/4/201921090개발 환경 구성: 431. Visual Studio 2019 - CMake를 이용한 공유/실행(so/out) 리눅스 프로젝트 설정파일 다운로드1
11832정성태3/4/201917017오류 유형: 524. Visual Studio CMake - rsync: connection unexpectedly closed
11831정성태3/4/201916839오류 유형: 523. Visual Studio 2019 - 새 창으로 뜬 윈도우를 닫을 때 비정상 종료
11830정성태2/26/201916538오류 유형: 522. 이벤트 로그 - Error opening event log file State. Log will not be processed. Return code from OpenEventLog is 87.
11829정성태2/26/201918288개발 환경 구성: 430. 마이크로소프트의 CoreCLR 프로파일러 예제 빌드 방법 - 리눅스 환경 [1]
11828정성태2/26/201926157개발 환경 구성: 429. Component Services 관리자의 RuntimeBroker 설정이 2개 있는 경우 [8]
11827정성태2/26/201919079오류 유형: 521. Visual Studio - Could not start the 'rsync' command on the remote host, please install it using your system package manager.
11826정성태2/26/201919256오류 유형: 520. 우분투에 .NET Core SDK 설치 시 패키지 의존성 오류
11825정성태2/25/201924479개발 환경 구성: 428. Visual Studio 2019 - CMake를 이용한 리눅스 빌드 환경 설정 [1]
11824정성태2/25/201918930오류 유형: 519. The SNMP Service encountered an error while accessing the registry key SYSTEM\CurrentControlSet\Services\SNMP\Parameters\TrapConfiguration. [1]
11823정성태2/21/201920617오류 유형: 518. IIS 관리 콘솔이 뜨지 않는 문제
11822정성태2/20/201918921오류 유형: 517. docker에 설치한 MongoDB 서버로 연결이 안 되는 경우
11821정성태2/20/201919684오류 유형: 516. Visual Studio 2019 - This extension uses deprecated APIs and is at risk of not functioning in a future VS update. [1]
11820정성태2/20/201922755오류 유형: 515. 윈도우 10 1809 업데이트 후 "User Profiles Service" 1534 경고 발생
11819정성태2/20/201922041Windows: 158. 컴퓨터와 사용자의 SID(security identifier) 확인 방법
11818정성태2/20/201920102VS.NET IDE: 131. Visual Studio 2019 Preview의 닷넷 프로젝트 빌드가 20초 이상 걸리는 경우 [2]
11817정성태2/17/201916450오류 유형: 514. WinDbg Preview 실행 오류 - Error : DbgX.dll : WindowsDebugger.WindowsDebuggerException: Could not load dbgeng.dll
11816정성태2/17/201919886Windows: 157. 윈도우 스토어 앱(Microsoft Store App)을 명령행에서 직접 실행하는 방법
11815정성태2/14/201918123오류 유형: 513. Visual Studio 2019 - VSIX 설치 시 "The extension cannot be installed to this product due to prerequisites that cannot be resolved." 오류 발생
11814정성태2/12/201916985오류 유형: 512. VM(가상 머신)의 NT 서비스들이 자동 시작되지 않는 문제
11813정성태2/12/201918318.NET Framework: 809. C# - ("Save File Dialog" 등의) 대화 창에 확장 속성을 보이는 방법
11812정성태2/11/201915627오류 유형: 511. Windows Server 2003 VM 부팅 후 로그인 시점에 0xC0000005 BSOD 발생
11811정성태2/11/201920832오류 유형: 510. 서버 운영체제에 NVIDIA GeForce Experience 실행 시 wlanapi.dll 누락 문제
... 76  77  78  79  80  81  82  83  [84]  85  86  87  88  89  90  ...