Json to CSharp

2023. 11. 13. 00:00CSharp/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 들을 들어가보자

힌트가 있을 것이다. 

 

아래와 같은 사이트가 보일 것이다. 

https://json2csharp.com/

 

Convert JSON to C# Classes Online - Json2CSharp Toolkit

 

json2csharp.com

이제 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

 

GCore.Source.JsonClassGenerator 1.16.0

Code generation

www.nuget.org

해당 page 에서 우측에 source repository 로 이동하면 아래와 같은 곳으로 이동된다.

https://github.com/KevinGliewe/GCore.Source

 

GitHub - KevinGliewe/GCore.Source: Code generator utils

Code generator utils. Contribute to KevinGliewe/GCore.Source development by creating an account on GitHub.

github.com

그리고 다음 source code 를 참조해보자 

https://github.com/KevinGliewe/GCore.Source/blob/master/src/GCore.Source.JsonClassGenerator/JsonClassGenerator.cs

 

이 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; }
}

 

수고 많았다. 이제 쉬도록 하자... 

 

관련영상

https://youtu.be/0Mhnq4ooZXM

반응형