2023. 11. 13. 00:00ㆍCSharp/Advance
프로그램을 작성하다 보면 json 문자열을 다루는 경우가 정말 많다.
요즘 대부분의 Http api 같은 경우는 json 을 이용하고
micro service 같은 경우는 input, output 의 표준이 되었다고 해도 과언이 아니다.
이러다보니 특정 json 문자열을 programming language 에 맞추어 해당 language 의 type 으로 만들어 주는 tool 들이 많이있다. 특히 인터넷에 online 으로 변경해주는 경우가 있으니 입맛에 맞추어 사용하면 된다.
하지만 어떤 경우는 runtime 중에 변환해야 하는 경우가 있다.
이런경우는 library 를 만들어서 json string 을 특정 class 로 변형해야 한다.
오늘은 json string 을 입력 받아 CSharp Class 파일을 generate 하는 방법을 알아볼 것이다.
Console app 을 하나 만들고 다음 nuget package 를 추가 하자
dotnet add package NJsonSchema.CodeGeneration.CSharp
dotnet add package NJsonSchema
이제 코드를 수정해 보자
JsonString 은 다음을 이용하자
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot",
"DatesAvailable": [
"2019-08-01T00:00:00-07:00",
"2019-08-02T00:00:00-07:00"
],
"TemperatureRanges": {
"Cold": {
"High": 20,
"Low": -10
},
"Hot": {
"High": 60,
"Low": 20
}
},
"SummaryWords": [
"Cool",
"Windy",
"Humid"
]
}
Program.cs
위 json string 을 file 로 읽어서 처리 해도 되고
다음과 같이 string 을 직접 입력해서 처리 해도 된다.
using NJsonSchema;
using NJsonSchema.CodeGeneration.CSharp;
using NJsonSchema.Generation;
string jsonString =
@"{
""Date"": ""2019-08-01T00:00:00-07:00"",
""TemperatureCelsius"": 25,
""Summary"": ""Hot"",
""DatesAvailable"": [
""2019-08-01T00:00:00-07:00"",
""2019-08-02T00:00:00-07:00""
],
""TemperatureRanges"": {
""Cold"": {
""High"": 20,
""Low"": -10
},
""Hot"": {
""High"": 60,
""Low"": 20
}
},
""SummaryWords"": [
""Cool"",
""Windy"",
""Humid""
]
}
";
var schemaGenerator = new SampleJsonSchemaGenerator();
JsonSchema schema = schemaGenerator.Generate(jsonString);
var generator = new CSharpGenerator(schema);
var file = generator.GenerateFile();
Console.WriteLine(file);
그럼 아래와 같은 output 이 생성 될 것이다.
//----------------------
// <auto-generated>
// Generated using the NJsonSchema v10.9.0.0 (Newtonsoft.Json v9.0.0.0) (http://NJsonSchema.org)
// </auto-generated>
//----------------------
namespace MyNamespace
{
#pragma warning disable // Disable all warnings
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.9.0.0 (Newtonsoft.Json v9.0.0.0)")]
public partial class TemperatureRanges
{
[Newtonsoft.Json.JsonProperty("Cold", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public Cold Cold { get; set; }
[Newtonsoft.Json.JsonProperty("Hot", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public Cold Hot { get; set; }
private System.Collections.Generic.IDictionary<string, object> _additionalProperties;
[Newtonsoft.Json.JsonExtensionData]
public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
{
get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary<string, object>()); }
set { _additionalProperties = value; }
}
}
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.9.0.0 (Newtonsoft.Json v9.0.0.0)")]
public partial class Cold
{
[Newtonsoft.Json.JsonProperty("High", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public int High { get; set; }
[Newtonsoft.Json.JsonProperty("Low", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public int Low { get; set; }
private System.Collections.Generic.IDictionary<string, object> _additionalProperties;
[Newtonsoft.Json.JsonExtensionData]
public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
{
get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary<string, object>()); }
set { _additionalProperties = value; }
}
}
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.9.0.0 (Newtonsoft.Json v9.0.0.0)")]
public partial class Anonymous
{
[Newtonsoft.Json.JsonProperty("Date", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public System.DateTimeOffset Date { get; set; }
[Newtonsoft.Json.JsonProperty("TemperatureCelsius", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public int TemperatureCelsius { get; set; }
[Newtonsoft.Json.JsonProperty("Summary", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string Summary { get; set; }
[Newtonsoft.Json.JsonProperty("DatesAvailable", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public System.Collections.Generic.ICollection<string> DatesAvailable { get; set; }
[Newtonsoft.Json.JsonProperty("TemperatureRanges", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public TemperatureRanges TemperatureRanges { get; set; }
[Newtonsoft.Json.JsonProperty("SummaryWords", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public System.Collections.Generic.ICollection<string> SummaryWords { get; set; }
private System.Collections.Generic.IDictionary<string, object> _additionalProperties;
[Newtonsoft.Json.JsonExtensionData]
public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
{
get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary<string, object>()); }
set { _additionalProperties = value; }
}
}
}
자 그런데 가만히 봐보면 c# class 에 너무 많은 data annotation 이 붙어 있다. ("[blabla]" <-- 요걸 의미함)
그리고 newtonsoft.json namespace 로 봐서 json.net 을 이용하는 것 같다.
물론 나쁘다는 것은 아니다.
codedom 관련 namespace 도 보인다.
물론 나쁘다는건 아니다.
하지만 아무래도 dotnet core 같은 곳에서 사용한다면 다른 방식이 좋을 것같다.
json.net 대신 System.Text.Json 을code dom 대신 roslyn 을 이용한다면
좀더 modern 할것 같은 느낌적인 느낌이다.
그래서 다른 것을 한번 찾아보자.
이전 강좌에서 문제를 해결하기 위해서 관점을 바꿔 보라고 했다.
자 이번에는 그렇다면 online 에서 json 을 c# 으로 변경해주는 site 들을 들어가보자
힌트가 있을 것이다.
아래와 같은 사이트가 보일 것이다.
이제 json 문자열을 넣고 다시 class 를 생성해 보자
public class Cold
{
public int High { get; set; }
public int Low { get; set; }
}
public class Hot
{
public int High { get; set; }
public int Low { get; set; }
}
public class Root
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string Summary { get; set; }
public List<DateTime> DatesAvailable { get; set; }
public TemperatureRanges TemperatureRanges { get; set; }
public List<string> SummaryWords { get; set; }
}
public class TemperatureRanges
{
public Cold Cold { get; set; }
public Hot Hot { get; set; }
}
어떤가? 너무 깔끔한 것 같지 않은가?
자 이 개발자가 코드를 공유하거나 nuget 같은 곳에 library 형태로 배포했기를 기도해 보자
그런데... 짜잔!!! 상단 오른쪽에 익숙한 마크가 보인다.... 그렇다 github 다 ㅋㅋㅋㅋ
github 에 따라 들어가 보자. 일단 license 부터 확인하자.
우리는 상용으로 쓸것은 아니라 크게 상관은 없을것 같지만...
또 이걸 쓰는 사용자들은 상용으로 쓸수도 있으니 꼭 license 를 자세히 보자
원본 source code 를 분석 하다 보면 아래와 같은 namespace 가 있다.
Xamasoft... 흠... 뭔가... feel 이 오지 않는가?
google 신에 도움을 받아 보자.
그렇다... 찾았다...
이제 저 class generator 를 nuget 에서 한번 검색해 보자
존재 한다. 이제 설치해 보자.
자 이제 실행해 보려고 하면.....
예제를 찾아야 하는데... 참 난감해 진다.
github 의 최신 코드와 nuget package 에 올라와 있는 내용이 다르다.
아마도 최신 버전을 nuget 에 publish 하지 않은 모양이다.
자 이쯤 해서.. 대부분의 사람들은 포기한다...
자 이제 잠시 숨을 고르자.
뇌에서는 계속적으로
포기해..
이걸 어떻게 사용해..
이게 뭐 중요하다고..
시간낭비야..
온갖 하지 않아도 될 이유를 만든다.
대부분이 그럴거다..
진정하고 자신에게 다음과 같이 이야기 해주고 다시 해보자
그래 알았어.. 근데 한번 더 해보자
결국 모든일에 성공여부는 이 일을 할 수 있다는 믿음에서 시작된다.
하찮은 일이란 없다. 그 일을 대하는 하찮은 태도만 있을 뿐이다.
잡설이 길었다....
자 이제 가만히 생각해보자. 어디서 부터 어떻게 해결해야 할까?
머리에서 생각나는 대로 가만히 둬 보자....
jsontocsharp, xamasoft, jsonclassgenerator, nuget....
nuget 에 새로운 것이 있을 수도 있다... 한번 찾아보자
어쩌면 새버전은 이름이 바뀐걸 수도 있다.
xamasoft 로 찾아봤으니 이번에는 jsonclassgenerator 라는 이름으로 찾아보자
응?!!!
GCore.Source.JsonClassGenerator..... 이라는게 있다. 뭔가 feel 이 온다.
일단 nuget pkg 홈페이지로 이동해 보자
https://www.nuget.org/packages/GCore.Source.JsonClassGenerator/1.16.0
해당 page 에서 우측에 source repository 로 이동하면 아래와 같은 곳으로 이동된다.
https://github.com/KevinGliewe/GCore.Source
그리고 다음 source code 를 참조해보자
이 source 안에서 Generate(string code, .....) 을 참조하자.
이제 class helper 를 만들어 보자
JsonClassGeneratorHelper.cs
public class JsonClassGeneratorHelper
{
public static string Generate(string jsonString)
{
return Generate(code: jsonString);
}
public static string Generate(
string code,
string MainClass = "Root",
string Namespace = null,
string SecondaryNamespace = null,
bool InternalVisibility = false,
bool ExplicitDeserialization = false,
bool UseNestedClasses = false,
bool ApplyObfuscationAttributes = false,
bool ExamplesInDocumentation = false,
bool UsePascalCase = false,
bool UseProperties = false,
string PropertyAttribute = null
)
{
var gen = new JsonClassGenerator
{
Example = code,
InternalVisibility = InternalVisibility,
CodeWriter = GetWriter(),
ExplicitDeserialization = ExplicitDeserialization,
Namespace = Namespace,
NoHelperClass = true,
SecondaryNamespace = SecondaryNamespace,
UseProperties = UseProperties,
MainClass = MainClass,
UsePascalCase = UsePascalCase,
//gen.PropertyAttribute = PropertyAttribute;
UseNestedClasses = UseNestedClasses,
ApplyObfuscationAttributes = ApplyObfuscationAttributes,
SingleFile = true,
ExamplesInDocumentation = ExamplesInDocumentation,
TargetFolder = null
};
gen.SingleFile = true;
using (var sw = new StringWriter())
{
gen.OutputStream = sw;
gen.GenerateClasses();
sw.Flush();
return sw.ToString();
}
}
private static ICodeWriter GetWriter() => new CSharpCodeWriter();
}
아래와 같이 사용해 보자
using JsonToCSharp;
string jsonString =
@"{
""Date"": ""2019-08-01T00:00:00-07:00"",
""TemperatureCelsius"": 25,
""Summary"": ""Hot"",
""DatesAvailable"": [
""2019-08-01T00:00:00-07:00"",
""2019-08-02T00:00:00-07:00""
],
""TemperatureRanges"": {
""Cold"": {
""High"": 20,
""Low"": -10
},
""Hot"": {
""High"": 60,
""Low"": 20
}
},
""SummaryWords"": [
""Cool"",
""Windy"",
""Humid""
]
}
";
var source = NJsonSchemaGeneratorHelper.Generate(jsonString);
var source2 = JsonClassGeneratorHelper.Generate(jsonString);
//Console.WriteLine(source);
Console.WriteLine(source2);
생성된 code 내용은 아래와 같다.
public class Cold
{
public int High;
public int Low;
}
public class Hot
{
public int High;
public int Low;
}
public class TemperatureRanges
{
public Cold Cold;
public Hot Hot;
}
public class Root
{
public DateTime Date;
public int TemperatureCelsius;
public string Summary;
public IList<DateTime> DatesAvailable;
public TemperatureRanges TemperatureRanges;
public IList<string> SummaryWords;
}
어떤가? 아주 깔끔해 졌지 않나?
처음에 사용한 NJsonSchemaGenerator 에 비하면 정말 simple 하다.
여기서 끝내도 좋다.
하지만... 최신버전을 사용하고 싶을 수도 있으니 xamasoft.jsonclassgenerator 의 github 로 다시 이동하자
이제 이것을 clone copy 하여 library 자체를 다운 받자..
앗 다운받고 보니 dotnet core 가 아니고 dotnet frameworks 4.6.1 버전이다.
자 이쯤해서 또 포기 하고 싶은가? 하하하하
원래 우리 뇌는 새로운것에 두려움을 갖고 있다.
나, 이글 읽고있는 당신, 빌게이츠, 일론머스크.. 모두 다 있다.
단지 그 두려움에 어떻게 대응 하느냐가 서로 다를 뿐이다.
자 결과를 상상해보자.
우린 분명 이 project 를 compile 해서 nuget package 로 만들고 core 용으로 사용할 것이다.
그게 잘 되어서 즐거워 하고 있는 자신을 상상해 보자.
이제 solution 을 열고 JsonClassGeneratorLib 프로젝트를 선택하고 마우스 우클릭하자
Upgrade click
Upgrade project to a newer .NET version 선택
In-place project upgrade 선택 --> Next
.NET 7.0 선택 --> Next
우측 상단 Upgrade selection click
1 failed 가 있는데 무시하자
JsonClassGeneratorLib 를 시작 project 로 설정 하자
build 해보자
위와 같은 error 가 발생할 것이다.
맨아래 Configuration 어쩌구를 double click 하자
하단 오른쪽에 빗자루를 클릭하자
그리고 build 를 다시 해보자 (해당 project 만 build 하자)
error list 가 있을텐데 다른 project 들에 대한 것이니 무시하자
output tab 에 가보면 정상 처리되어 있을것이다.
이제 JsonClassGeneratorLib 로 가서 마우스 우클릭 후 pack 을 실행하자
(실행전에 release, any cpu 선택하는 것 잊지 말자. 무조건 release 로 선택되게 바뀌었다고 하는데.. 흠..)
그러면 local nuget package 가 나온다.
local nuget package 를 참조하자.
그리고 프로젝트를 하나 더 만들어서 해당 package 를 참조하자
JsonClassGeneratorHelper.cs
using Xamasoft.JsonClassGenerator;
namespace JsonToCSharpNew;
public class JsonClassGeneratorHelper
{
public static string Generate(string jsonString)
{
var generator = new JsonClassGenerator();
var builder = generator.GenerateClasses(jsonString, out _);
return builder.ToString();
}
}
아래와 같이 사용해보자
using JsonToCSharpNew;
string jsonString =
@"{
""Date"": ""2019-08-01T00:00:00-07:00"",
""TemperatureCelsius"": 25,
""Summary"": ""Hot"",
""DatesAvailable"": [
""2019-08-01T00:00:00-07:00"",
""2019-08-02T00:00:00-07:00""
],
""TemperatureRanges"": {
""Cold"": {
""High"": 20,
""Low"": -10
},
""Hot"": {
""High"": 60,
""Low"": 20
}
},
""SummaryWords"": [
""Cool"",
""Windy"",
""Humid""
]
}
";
var source = JsonClassGeneratorHelper.Generate(jsonString);
Console.WriteLine(source);
아래와 같은 code 가 생성될 것이다.
// Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
public class Cold
{
public int High { get; set; }
public int Low { get; set; }
}
public class Hot
{
public int High { get; set; }
public int Low { get; set; }
}
public class Root
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string Summary { get; set; }
public List<DateTime> DatesAvailable { get; set; }
public TemperatureRanges TemperatureRanges { get; set; }
public List<string> SummaryWords { get; set; }
}
public class TemperatureRanges
{
public Cold Cold { get; set; }
public Hot Hot { get; set; }
}
수고 많았다. 이제 쉬도록 하자...
관련영상
'CSharp > Advance' 카테고리의 다른 글
알수 없는 문제를 해결하는 방법 - vite dev server 의 404 오류 해결 (0) | 2023.11.06 |
---|---|
Refactroing - HttpClient(EP02) (1) | 2023.10.30 |
Refactroing - HttpClient (1) | 2023.10.23 |
Refactoring - Encryption Hash Helper (0) | 2023.10.16 |
C# deadlock prevent (0) | 2023.06.23 |