성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Reordering on an Alpha processor ;...
[정성태] 공유 감사합니다. ^^ 참고로, WPF에서 WindowsF...
[Tom Lee] 답변 감사합니다. 나름의 해결책 연구해보고 여기에도 공유해봅니다...
[정성태] 아래의 글을 보면, MoveWindow 하면 될 듯한데요. ^^...
[Tom Lee] 안녕하세요 올려주신 글 참고하여 WPF 어플리케이션 안에 Uni...
[정성태] A graphical depiction of the steps ...
[정성태] 질문을 주셔서 출판사 측에 문의를 했습니다. 약 한 달 정도 후...
[Thorondor
] @정성태 개인 블로그인데도 거의 커뮤니티 급 인 것 같아요. 요...
[정성태] Roll A Lisp In C - Reading ; https...
[정성태] Java - How to use the Foreign Funct...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>JavaScriptSerializer, DataContractJsonSerializer, Json.NET</h1> <p> <br /> 닷넷에는 기본적으로 JavaScriptSerializer, DataContractJsonSerializer 타입이 JSON 직렬화와 관련되어 제공됩니다. 각각 장단점이 있는데요. JavaScriptSerializer는 부가적인 작업 없이 모든 객체의 값을 Key/Value의 쌍으로 직렬화/역직렬화할 수 있습니다. 즉, strong-type이 제공되지 않습니다. 이를 보완하기 위해 클래스를 이용한 strong-type을 지원하는 DataContractJsonSerializer를 사용할 수 있는데, 당연히 클래스 구조는 그에 맞게 만들어 주어야 합니다.<br /> <br /> 예를 들어, 다음은 페이스북의 me 쿼리의 일부를 표현한 것인데요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > { "id":"ttt", "work":[ {"employer":{"id":"zzz","name":"..."}, "start_date":"1992-01", } ], "timezone":9, "verified":true, "updated_time":"2012-11-16T07:23:07+0000" } </pre> <br /> DataContractJsonSerializer로 역직렬화하기 위해 다음과 같이 클래스들을 만들어 주고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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; } } </pre> <br /> 이렇게 코딩을 해주면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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(<a target='tab' href='http://www.sysnet.pe.kr/2/0/1390'>StringToStream</a>(json)) as GraphUser; Console.WriteLine(user2.id); Console.WriteLine(user2.work[0].employer.id); } </pre> <br /> <hr style='width: 50%' /><br /> <br /> 그런데, 여기서 문제가 있습니다. 만약 string 데이터에 "\n"를 포함하게 되면 DataContractJsonSerializer.ReadObject 호출 시에 예외가 발생합니다. 예를 들어, 위의 json 문자열의 id 값이 다음과 같은 경우입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > string json = "{ \"id\":\"t<span style='color: blue; font-weight: bold'>\n</span>tt\", ...[생략]... }"; </pre> <br /> 그럼, 이런 예외 메시지가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>Unhandled Exception: System.Runtime.Serialization.SerializationException: Therewas an error deserializing the object of type ConsoleApplication1.GraphUser. Encountered invalid character ''. ---> System.FormatException: Encountered invalid character ''.</span> 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 </pre> <br /> 문제의 원인은 "\n" 문자에서 역슬래시가 1개가 아닌 2개여야 한다는 점입니다. 그래서, 다음과 같이 변경해 주면 정상적으로 역직렬화를 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > string json = "{ \"id\":\"t<span style='color: blue; font-weight: bold'>\\n</span>tt\", ...[생략]... }"; // 또는 string json = "{ \"id\":\"t<span style='color: blue; font-weight: bold'>\\u000a</span>tt\", ...[생략]... }"; </pre> <br /> 그런데, 이런 경우가 왜 발생할까요? 물론 DataContractJsonSerializer로 직렬화했다면 저런 식으로 문자열이 나오지 않습니다. 제가 경험했던 곳은 바로 페이스북의 /me 쿼리에서 저런 결과를 볼 수 있었습니다. 따라서, 페이스북으로부터 json 문자열을 받는다면 반드시 '\n' 문자를 '\\n'으로 치환해 주는 작업이 선행되어야 합니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> \n 문자에 대한 처리를 하지 않고 역직렬화를 하고 싶다면 Json.NET을 이용하면 됩니다. (다운로드 및 그에 대한 소스 코드는 모두 공개되어 있습니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Json.NET ; <a target='tab' href='http://json.codeplex.com/'>http://json.codeplex.com/</a> </pre> <br /> Nuget을 지원하기 때문에 Visual Studio 2012에서 "View" / "Other Windows" / "Package Manager Console" 창을 띄우고, 다음과 같이 입력하면 Json.NET 이 프로젝트에 추가됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Package Manager Console Host Version 2.1.31002.9028 Type 'get-help NuGet' to see all available NuGet commands. PM> <span style='color: blue; font-weight: bold'>Install-Package Newtonsoft.Json</span> Successfully installed 'Newtonsoft.Json 4.5.11'. Successfully added 'Newtonsoft.Json 4.5.11' to ConsoleApplication1. PM> </pre> <br /> 다음은 Json.NET을 이용한 간단한 코드입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > string json = "{ \"id\":\"t<span style='color: blue; font-weight: bold'>\n</span>tt\", \"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); </pre> <br /> Json.NET이 좋은 또 다른 이유가 하나 있습니다. 바로 DateTime 값의 역직렬화입니다. 예제의 json 문자열에 보면 updated_time의 값이 "2012-11-16T07:23:07+0000"인데 이를 그냥 "string" 타입으로 역직렬화하고 있지만, 만약 이 필드의 타입을 DateTime으로 바꾸게 되면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public class GraphUser { public string id { get; set; } public GraphWork[] work { get; set; } public int timezone { get; set; } public bool verified { get; set; } <span style='color: blue; font-weight: bold'>public DateTime updated_time { get; set; }</span> } </pre> <br /> DataContractJsonSerializer으로 역직렬화하는 경우 다음과 같은 예외가 발생합니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> There was an error deserializing the object of type ConsoleApplication1.GraphUser. DateTime content '2012-11-16T07:23:07+0000' does not start with <a target='tab' href='http://www.sysnet.pe.kr/2/0/1101'>'\/Date(' and end with ')\/'</a> as required for JSON.<br /> </div><br /> <br /> 이런 상황에서 표준이냐 아니냐의 문제는 중요하지 않습니다. (그 유명한 페이스북에서 이런 식으로 데이터를 넘겨주기 때문에. ^^;)<br /> <br /> 하지만, 역시 이를 Json.NET으로 역직렬화하면 잘 됩니다. "2012-11-16T07:23:07+0000" UTC 시간이므로 변환하고 나면 한글 윈도우의 경우 +9 시간대를 적용받아 "{2012-11-16 16:23:07}" 값으로 계산합니다.<br /> <br /> 결론은, 3가지 Json 직렬화 방법 중에서 가능한 Json.NET을 이용하시는 것이 좋습니다. ^^<br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=756&boardid=331301885'>첨부한 파일은 위의 간단한 코드를 담고 있는 테스트 프로젝트</a>입니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1863
(왼쪽의 숫자를 입력해야 합니다.)