티스토리 뷰

개발/C#

[C#/JSON] JSON parser 구현

KIBBOMI 2021. 1. 2. 22:40

XML과 비슷하게 JSON도 데이터 전송에 있어서 중요한 역할을 한다. JSON은 JavaScript Object Notation 으로, 특별한 자료구조가 아닌 그냥 JS에서 쓰던 object구조랑 똑같다.

개인적인 생각이지만 xml보단 json을 많이 쓰게 되지 않을까 싶다(조사나 통계를 찾아보지 않았지만 지금도 JSON을 많이 쓰고 있을 것 같다). Docker에서는 Json과 yaml파일만 중점으로 다루는데 역시 xml보단 json이 더 중요하지 않을까 싶다. 그래도 가독성은 xml이 조금 더 좋은 것 같다.

여튼.. JSON 데이터를 받을 때 어떻게 파싱할 것인지 간단하게 알아보자.


1. C#에서의 JSON parsing

우선, JSON의 구조에 대해서는 다 알고 있다고 가정하겠습니다.

배열[], 객체{}, 값으로 올 수 있는 것들은 string, number, object, array, boolean, null 등..

 

1.1) parsing에 앞서..

C#에서 XML에 대한 기능은 제공하지만 JSON은 제공하지 않습니다. 그래서 NuGet package에서 다운로드를 받아야 합니다.

Visual studio 2017 한글판 기준입니다.

 

Newtonsoft.Json

Newtonsoft.Json패키지를 설치해줍시다. 다운로드 수를 보아하니 상당히 많이 쓰이고 있음을 알 수 있습니다.

 

자동으로 연결시켜주고, 솔루션 탐색기에서 프로젝트의 참조란을 보시면 Newtonsoft.Json이 참조되어 있음을 알 수 있습니다. 이제 using 문을 사용해서 사용하면 됩니다.(using Newtonsoft.Json)

 

1.2) 간단한 예제

배열도 없고, 단순히 객체 1개만 있는 예를 들어보면, (출처는 w3schools.com입니다. note.xml을 json형태로 바꾼 것)

 

{
  "to":"Tove",
  "from":"Jani",
  "heading":"Reminder",
  "body":"Don't forget me this weekend!"
}

위 내용을 파싱해보면...

using System;
using System.Collections.Generic;
using System.Collections;
using Newtonsoft.Json.Linq; //For JObject

namespace ConsoleApp1
{
    class cstest
    {
        static void Main(string[] args)
        {
            
            try
            {
                string JsonText = System.IO.File.ReadAllText("Note.json");

                JObject JsonData = JObject.Parse(JsonText);
                //=JObject.Parse(string Json type text) -> Json type text를 갖고 있는 string이 매개변수.

                Console.WriteLine($"to : {JsonData["to"]}");
                Console.WriteLine($"from : {JsonData["from"]}");
                Console.WriteLine($"heading : {JsonData["heading"]}");
                Console.WriteLine($"body : {JsonData["body"]}");

            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
            return;
        }
    }
}

위의 짧은 코드로 구현할 수 있다. 출력단만 제외하면 '2줄'이면 된다.

 

지금은 파일로 읽었지만,, 원래라면 api 등으로 http stream으로 넘어올테니 값이 string에 저장될 것이라는 개념은 똑같다. string변수를 선언하고 싶지 않다면. Parse의 파라미터에 바로. ReadAllText를 넣어주어도 된다. 그러면 단 한 줄로 해결할 수 있겠다.

위 코드 실행결과

잘 동작하는 것을 볼 수 있다.

 

다음은 조금 더 복잡하게,

 

1.3) 복잡한  예제

다음 Json 예제는 json.org/example.html의 내용입니다.

{
    "glossary": {
        "title": "example glossary",
		"GlossDiv": {
            "title": "S",
			"GlossList": {
                "GlossEntry": {
                    "ID": "SGML",
					"SortAs": "SGML",
					"GlossTerm": "Standard Generalized Markup Language",
					"Acronym": "SGML",
					"Abbrev": "ISO 8879:1986",
					"GlossDef": {
                        "para": "A meta-markup language, used to create markup languages such as DocBook.",
						"GlossSeeAlso": ["GML", "XML"]
                    },
					"GlossSee": "markup"
                }
            }
        }
    }
}

슬슬 눈이 아파오기 시작하는...

자세히 보면 객체 안에 객체가 있고.. 또 어떤 값은 배열로 되어있는 것을 볼 수 있다.

 

이럴 때는 값을 어떻게 파싱해야할까?

using System;
using System.Collections.Generic;
using System.Collections;
using Newtonsoft.Json.Linq; //For JObject

namespace ConsoleApp1
{
    class cstest
    {
        static void Main(string[] args)
        {

            try
            {
                JObject JsonData = JObject.Parse(System.IO.File.ReadAllText("glossary.json"));


                Console.WriteLine($"glossary.title : {JsonData["glossary"]["title"]}");

                Console.WriteLine($"glossary.GlossDiv.title : {JsonData["glossary"]["GlossDiv"]["title"]}");

                Console.WriteLine($"glossary.GlossDiv.GlossList.GlossEntry.ID : " +
                    $"{JsonData["glossary"]["GlossDiv"]["GlossList"]["GlossEntry"]["ID"]}");

                Console.WriteLine($"glossary.GlossDiv.GlossList.GlossEntry.Abbrev : " +
                    $"{JsonData["glossary"]["GlossDiv"]["GlossList"]["GlossEntry"]["Abbrev"]}");

                //JArray JsonArray = (JArray)JsonData["glossary"]["GlossDiv"]["GlossList"]["GlossEntry"]["GlossDef"]["GlossSeeAlso"];
                //foreach(JToken item in JsonArray)
                    //Console.WriteLine($"glossary.GlossDiv.GlossList.GlossEntry.GlassDef.GlossSeeAlso : {item.ToString()}");

                foreach (JToken item in JsonData["glossary"]["GlossDiv"]["GlossList"]["GlossEntry"]["GlossDef"]["GlossSeeAlso"])
                 Console.WriteLine($"glossary.GlossDiv.GlossList.GlossEntry.GlossSeeAlso : {item.ToString()}");

                /*Console.WriteLine($"glossary.GlossDiv.GlossList.GlossEntry.GlossSeeAlso : " +
                $"{JsonData["glossary"]["GlossDiv"]["GlossList"]["GlossEntry"]["GlossDef"]["GlossSeeAlso"][0]}");
                Console.WriteLine($"glossary.GlossDiv.GlossList.GlossEntry.GlossSeeAlso : " +
               $"{JsonData["glossary"]["GlossDiv"]["GlossList"]["GlossEntry"]["GlossDef"]["GlossSeeAlso"][1]}");*/
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
            return;
        }
    }
}

 

위 코드를 보면....  읽고 싶지도 않을 것으로 예상된다.

필요한 부분만 보자. 애초에 JSON형태부터 복잡한 형태라 코드도 길어질 수밖에 없다.

위 코드 실행결과

이전에서 읽었던 것처럼 JSON을 JObject에 읽어 들인다.

출력할 때 옆에 적어놓은 glossary.title처럼 일반 구조체 같은 경우는 ' . ' 으로 멤버 변수에 접근하게 되는데 여기서는 key를 사용하여 ["key"]와 같은 방식으로 접근해서 들어간다고 생각하면 될 것 같다.

 

그래서, 객체 안에 객체 안에 객체가 있더라도 [][][]를 사용해서 계속 들어가면 된다.

 

이제, 배열의 경우를 알아보자, 배열은 JArray를 사용해서 가질 수 있다.

JObject["key1"]["key2"]같은경우 Type은 JToken이다. 하나의 key-value를 JToken이라고 하는 것 같다. 그래서, 이 value가 Array인 경우 JArray로 형변환하여 foreach문으로 모두 접근할 수 있다.

혹은 [0], [1]처럼 기존과 같은 형태로 접근할 수 있다.

 

배열을 출력하는 3가지 방법을 코드로 나타내었고 3가지 중 몇 방법은 주석처리로 해놓았다.


이 정도만 알아도

JSON파싱에서 단순한 객체, 객체 안의 객체, 배열들만 해도 복잡한 JSON객체가 오더라도 충분히 응용해서 접근할 수 있다고 생각합니다.

 

결론은, API가 됐든, File이 됐든, JObject 변수에 읽어서 [] 연산자와 key를 사용해서 value에 접근한다.

이 큰 개념만 잡고 있다면 충분하다고 봅니다.

 

또, JSON 객체를 생성하는 것도 언젠가 다뤄야 할 것 같습니다.

필요하다고 생각될 때, 공부해서 공유하도록 하겠습니다. ^_^

 

지적 댓글 환영입니다!

감사합니다

댓글
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Total
Today
Yesterday