머리말
본 포스팅이 C# 개발환경에서 JSON 사용방법을 설명하는 마지막 포스팅이다. 첫 번째 포스팅에서는 JSON의 배경과 구조에 대한 설명, 두 번째 포스팅에서는 직렬화 및 역직렬화에 대한 설명을 담았다. 필요하다면 포스팅 상단의 도움이 될만한 포스팅 영역에 링크를 남겨놓았으니 참고하길 바란다. 본 포스팅에서는 Newtonsoft.Json 라이브러리와 System.Text.Json 라이브러리에 대해 간략한 사용 방법을 설명한다.
Newtonsoft.Json 라이브러리
Newtonsoft.Json 라이브러리는 NuGet 패키지 관리자로 간편하게 설치할 수 있다. Visual Studio 개발 도구의 [도구] - [NuGet 패키지 관리자(N)] -[솔루션용 NuGet 패키지 관리...] 메뉴를 클릭하면 패키지 관리자 화면으로 이동된다. 왼쪽의 찾아보기 영역에서 Newtonsoft.Json을 검색하면 설치할 수 있다. 지금까지 약 932만 번 패키지가 다운로드되었다는 사실도 알려준다.
라이브러리는 10초 내외로 설치가 완료된다. 별도의 추가 설정 없이 해당 라이브러리를 바로 사용할 수 있다. Native Object, 직렬화 및 역직렬화를 사용하는 예제는 아래와 같이 코드를 공개한다. Native Object 개념은 두 번째 포스팅에 자세히 설명했으니 참고하길 바란다. 코드 중간에 주석으로 간단하게 설명을 달아 놓았으며 JSON은 파일 읽기 및 웹 전송 등 다양한 방식으로 string 변수에 담을 수 있으니, 본 포스팅에서는 string 변수에 로드가 완료되었다는 가정하에 미리 string 변수에 담아서 설명하도록 한다.
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Wookoa
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// JSON Data
string json = "" +
"{ " +
" 'squadName': 'Super hero squad', " +
" 'homeTown': 'Metro City', " +
" 'active': true, " +
" 'members': [ " +
" { 'name': 'Molecule Man', " +
" 'age': 29, " +
" 'powers': [ " +
" 'Radiation resistance', " +
" 'Turning tiny', " +
" ] " +
" }, " +
" { " +
" 'name': 'Madame Uppercut', " +
" 'age': 39, " +
" 'powers': [ " +
" 'Million tonne punch', " +
" ] " +
" }, " +
" { " +
" 'name': 'Eternal Flame', " +
" 'age': 1000000, " +
" 'powers': [ " +
" 'Immortality', " +
" 'Heat Immunity', " +
" ] " +
" } " +
" ] " +
"}";
/////////////////////////////////////////////////
// Native Object 사용 방법
/////////////////////////////////////////////////
// Native Object 생성
JObject jObject = JObject.Parse(json);
// Json Data 전체 출력
Console.WriteLine(jObject.ToString());
// JSON 데이터 중 'Radiation resistance' 데이터까지 접근하는 방법
Console.WriteLine(jObject["members"][0]["powers"][0]);
// JSON 데이터 하위 객체인 members 객체의 name 값을 반복적으로 접근하는 방법
JToken jToken = jObject["members"];
foreach (JToken members in jToken)
{
Console.WriteLine(members["name"]);
}
/////////////////////////////////////////////////
// 역직렬화(Deserialize) 방법
/////////////////////////////////////////////////
// 역직렬화 수행 후 미리 선언한 클래스에 저장
Root rootObject = JsonConvert.DeserializeObject<Root>(json);
// JSON 데이터 중 'Radiation resistance' 데이터까지 접근하는 방법
Console.WriteLine(rootObject.members[0].powers[0]);
// JSON 데이터 하위 객체인 members 객체의 name 값을 반복적으로 접근하는 방법
foreach (Members members in rootObject.members)
{
Console.WriteLine(members.name);
}
/////////////////////////////////////////////////
// 직렬화(Serialize) 방법
/////////////////////////////////////////////////
// 직렬화 수행 후 string 변수에 저장
string serializeResult = JsonConvert.SerializeObject(rootObject);
// Json Data 전체 출력
Console.WriteLine(serializeResult);
}
// 역직렬화를 위한 클래스 선언
public class Root
{
public string squadName;
public string homeTown;
public string active;
public List<Members> members;
}
public class Members
{
public string name;
public string age;
public List<string> powers;
}
}
}
위의 소스에서 설명하는 JSON은 다중으로 중첩된 다소 복잡한 구조다. 위의 JSON 예제로 이해가 된다면 더욱 복잡한 구조의 JSON도 이해하는데 어려움이 없을 것이다. 기본적으로 JSON의 데이터 포맷은 'squadName': 'Super hero squad' 와 같이 key:value 한 쌍으로 구성된다. 하지만 key 값이 없는 구조도 존재하는데, 그것을 프로퍼티(Property)라 부른다. 이와 같이 소스 코드에서 설명하는 JSON은 members 객체가 프로퍼티의 배열로 구성되었으며, powers 객체는 프로퍼티로 구성되었다.
먼저 Native Object 사용 방법을 설명한다. JSON 데이터를 저장하기 위해 미리 클래스를 정의할 필요가 없다. 라이브러리에서 제공하는 JObject 객체를 사용하면 된다. 위의 예제와 같이 parse 함수를 이용해서 JSON을 JObject 객체에 저장한다. 여기서 사용하는 JObject 객체가 바로, Newtonsoft.Json 라이브러리에서 제공하는 Native Object가 된다. 사용 방법은 매우 직관적이다. 키 벨류(key:value)로 구성된 데이터는 브래킷 사이에 변수명을 직접 입력해서 값을 불러올 수 있으며, 프로퍼티로 구성된 데이터는 브라켓 사이에 인덱스를 입력해서 값을 불러올 수 있다. 두 번째 포스팅에서 설명했듯이 Native Object를 사용한 방식은 키값을 직접 입력해야 하기 때문에 동적으로 변화하는 JSON 데이터에는 사용할 수 없다. 일종의 하드코딩이 필요하기 때문이다. Native Object의 하위 오브젝트를 추출하기 위해서는 JToken 객체가 필요하다. JToken 객체는 JObject 객체의 추상화 클래스로써 Newtonsoft.Json 라이브러리에서만 존재하는 개념이다. 위의 소스코드는 JToken 객체를 이용해서 members 데이터를 반복적으로 출력하는 예제이다.
다음으로, 역직렬화(Deserialize) 방법을 설명한다. 역직렬화를 수행하기 위해서는 JSON 데이터와 동일한 구조의 클래스가 필요하다. 본 예제는 조금 복잡한 구조의 JSON 데이터이기 때문에 중첩된 클래스가 필요하다. Members 클래스는 name, age, List<string> 필드를 갖고 있으며, 최종적으로 Root 클래스는 squadName, homeTown, active, List<Members> 필드를 갖고 있다. JSON 데이터와 동일하게 클래스만 잘 생성한다면 역직렬화 사용은 어렵지 않다.
마지막으로, 직렬화(Serialize) 방법을 설명한다. 직렬화는 역직렬화로 생성된 클래스를 다시 string 변수에 데이터를 옮겨 담는다. 제공된 함수만 사용하면 쉽게 역직렬화를 수행할 수 있다.
System.Text.Json 라이브러리
마찬가지로, NuGet 패키지 관리자로 쉽게 설치할 수 있다. Visual Studio 개발 도구의 [도구] - [NuGet 패키지 관리자(N)] -[솔루션용 NuGet 패키지 관리...] 메뉴를 클릭하면 패키지 관리자 화면으로 이동된다. 왼쪽의 찾아보기 영역에서 System.Text.Json을 검색하면 설치할 수 있다. 벌써 95만 번 정도 패키지가 다운로드되었다.
비교적 참조 파일이 많이 설치된다. 마찬가지로 별도의 설정 없이 바로 사용할 수 있다. Newtonsoft.Json 라이브러리 설명과 동일한 방법으로 아래 코드와 같이 설명한다.
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Wookoa
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// JSON Data
string json = @"" +
@"{ " +
@" ""squadName"": ""Super hero squad"", " +
@" ""homeTown"": ""Metro City"", " +
@" ""active"": ""true"", " +
@" ""members"": [ " +
@" { ""name"": ""Molecule Man"", " +
@" ""age"": ""29"", " +
@" ""powers"": [ " +
@" ""Radiation resistance"", " +
@" ""Turning tiny"", " +
@" ] " +
@" }, " +
@" { " +
@" ""name"": ""Madame Uppercut"", " +
@" ""age"": ""39"", " +
@" ""powers"": [ " +
@" ""Million tonne punch"", " +
@" ] " +
@" }, " +
@" { " +
@" ""name"": ""Eternal Flame"", " +
@" ""age"": ""1000000"", " +
@" ""powers"": [ " +
@" ""Immortality"", " +
@" ""Heat Immunity"", " +
@" ] " +
@" } " +
@" ] " +
@"}";
/////////////////////////////////////////////////
// Native Object 사용 방법
/////////////////////////////////////////////////
// Native Object 생성
JsonDocumentOptions jsonDocumentOptions = new JsonDocumentOptions
{
AllowTrailingCommas = true // 데이터 후행의 쉼표 허용 여부
};
JsonDocument jsonDocument = JsonDocument.Parse(json, jsonDocumentOptions);
// Json Data 전체 출력
Console.WriteLine(jsonDocument.RootElement);
// JSON 데이터 중 'Radiation resistance' 데이터까지 접근하는 방법
Console.WriteLine(jsonDocument.RootElement.GetProperty("members")[0].GetProperty("powers")[0]);
// JSON 데이터 하위 객체인 members 객체의 name 값을 반복적으로 접근하는 방법
JsonElement jsonElement = jsonDocument.RootElement.GetProperty("members");
foreach (JsonElement members in jsonElement.EnumerateArray())
{
Console.WriteLine(members.GetProperty("name"));
}
/////////////////////////////////////////////////
// 역직렬화(Deserialize) 방법
/////////////////////////////////////////////////
// 역직렬화 수행 후 미리 선언한 클래스에 저장
JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions
{
AllowTrailingCommas = true, // 데이터 후행의 쉼표 허용 여부
};
Root rootObject = JsonSerializer.Deserialize<Root>(json, jsonSerializerOptions);
// JSON 데이터 중 'Radiation resistance' 데이터까지 접근하는 방법
Console.WriteLine(rootObject.members[0].powers[0]);
// JSON 데이터 하위 객체인 members 객체의 name 값을 반복적으로 접근하는 방법
foreach (Members members in rootObject.members)
{
Console.WriteLine(members.name);
}
/////////////////////////////////////////////////
// 직렬화(Serialize) 방법
/////////////////////////////////////////////////
// 직렬화 수행 후 string 변수에 저장
string serializeResult = JsonSerializer.Serialize(rootObject);
// Json Data 전체 출력
Console.WriteLine(serializeResult);
}
// 역직렬화를 위한 클래스 선언
public class Root
{
[JsonInclude]
public string squadName;
[JsonInclude]
public string homeTown;
[JsonInclude]
public string active;
[JsonInclude]
public List<Members> members;
}
public class Members
{
[JsonInclude]
public string name;
[JsonInclude]
public string age;
[JsonInclude]
public List<string> powers;
}
}
}
전체적으로 Newtonsoft.Json 패키지와 비슷한 방법으로 JSON 데이터에 접근이 가능하다. 비교적 JSON 데이터를 세세하게 컨트롤할 수 있는 옵션이 많이 제공된다. 반대로 말하면 JSON 데이터의 형식을 민감하게 처리하고 있다. 이를테면 JSON 데이터 중 싱글 쿼테이션은 오류를 리턴하거나 값의 데이터 형식이 일치하지 않으면 오류를 리턴하고 있다. 정확하게 일치하는 데이터만 성공적으로 처리하겠다는 의지가 돋보인다. 또한 .NET Core 개발팀에서 배포하는 라이브러리라서 그런지, getProperty 함수 또는 RootElement 필드가 C# 문법과 잘 어울리는 모양새다.
소스 코드의 주석만으로도 이해하는데 크게 어렵지 않을 것으로 생각된다. 함수명만 조금씩 다를 뿐 Newtonsoft.Json 라이브러리와 동일한 방법으로 수행되기 때문이다. 차이점이 있다면 JsonElement 객체는 Newtonsoft.Json 라이브러리의 JToken과 대응되는 객체이다.
꼬리말
기나긴 설명 끝에 C# 개발환경에서 JSON 포맷을 다루는 포스팅을 끝마치게 되었다. 전체적으로 내용이 상당히 길었지만 라이브러리를 이해하는데 꼭 필요한 부분만 설명하려다 보니 기본적인 내용만 소개하게 되어 약간의 아쉬움이 남는다. 처음 포스팅을 작성할 때에는 소스 코드 위주로 설명하려 했으나 기본적인 JSON을 다루기 위한 소스 코드는 비교적 간단했다. 다양한 상황에서 데이터를 처리하는 방법에 대해서는 다음 시간을 기약하며 본 포스팅의 시리즈는 여기서 마무리 짓도록 한다.
소중한 댓글 (0)