성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
글쓰기
제목
이름
암호
전자우편
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'>C# - .NET 8 JsonStringEnumConverter의 AOT를 위한 개선</h1> <p> System.Text.Json은,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > .NET 6부터 SourceGenerator와 통합된 System.Text.Json ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13214'>https://www.sysnet.pe.kr/2/0/13214</a> </pre> <br /> 컴파일 시점에 소스 코드 생성을 통해 Json 직렬화에 대한 AOT 친화력을 높였습니다. 하지만 enum의 경우에는 약간 부족한 면이 있었는데요, 원래 enum은 직렬화를 했을 때 숫자로 바뀌는 것이 기본 동작입니다. 간단하게 예를 들어 볼까요? ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Text.Json; using System.Text.Json.Serialization; namespace ConsoleApp1; internal class Program { static void Main(string[] args) { MyPerson t = new MyPerson { Language = Language.Cpp }; string jsonString = JsonSerializer.Serialize(t, MyContext.Default.MyPerson); Console.WriteLine(jsonString); // 출력 결과: {<span style='color: blue; font-weight: bold'>"Language":1</span>} MyPerson clone = JsonSerializer.Deserialize(jsonString, MyContext.Default.MyPerson); Console.WriteLine(clone.Language); // 출력 결과: Cpp } } public struct MyPerson { public Language Language { get; set; } } <span style='color: blue; font-weight: bold'>public enum Language { CSharp, Cpp, Python, Go }</span> [JsonSerializable(typeof(MyPerson))] public partial class MyContext : JsonSerializerContext { } </pre> <br /> 보는 바와 같이 enum 타입에 대한 직렬화가 숫자, 위의 경우 Cpp에 해당하는 1로 처리되었습니다. 사실 이렇게만 처리하려고 한다면 AOT 빌드에서도 잘 동작하므로 문제 될 것이 없습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그런데, 만약 이것을 "문자열"로 하고 싶다면 어떻게 해야 할까요? 즉, 1이 아닌 다음과 같이 문자열로 표현하고 싶다면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > {"Language":<span style='color: blue; font-weight: bold'>"Cpp"</span>} </pre> <br /> 기존에는 그런 경우, .NET Core 3.0부터 추가된 <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonstringenumconverter'>JsonStringEnumConverter</a>를 사용하면 됐습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 1) 필드에 직접 설정하거나, public struct MyPerson { [JsonConverter(typeof(<span style='color: blue; font-weight: bold'>JsonStringEnumConverter</span>))] public Language Language { get; set; } } </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 2) enum 타입에 설정 [JsonConverter(typeof(<span style='color: blue; font-weight: bold'>JsonStringEnumConverter</span>))] public enum Language { CSharp, Cpp, Python, Go } </pre> <br /> 문제는, JsonStringEnumConverter 내부에서 저런 처리를 위해 동적으로 코드 생성을 하기 때문에 AOT 빌드 시에는 사용할 수 없다는 점입니다. 그래서 위의 상태에서 PublishAot 옵션을 주고 빌드하면 컴파일할 때도 이런 경고가 나오지만,<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'> ILC : AOT analysis warning IL3050: ConsoleApp1.MyEnum: Using member 'System.Text.Json.Serialization.JsonStringEnumConverter.JsonStringEnumConverter()' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. JsonStringEnumConverter cannot be statically analyzed and requires runtime code generation. Applications should use the generic JsonStringEnumConverter<TEnum> instead.<br /> </div><br /> <br /> AOT 빌드 후 실행할 때도 이런 오류가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Unhandled Exception: System.NotSupportedException: 'System.Text.Json.Serialization.Converters.EnumConverter`1[ConsoleApp1.MyEnum]' is missing native code or metadata. This can happen for code that is not compatible with trimming or AOT. Inspect and fix trimming and AOT related warnings that were generated when the app was published. For more information see https://aka.ms/nativeaot-compatibility at System.Reflection.Runtime.General.TypeUnifier.WithVerifiedTypeHandle(RuntimeConstructedGenericTypeInfo, RuntimeTypeInfo[]) + 0x75 at System.Text.Json.Serialization.Converters.EnumConverterFactory.Create(Type, EnumConverterOptions, JsonNamingPolicy, JsonSerializerOptions) + 0x17 at ConsoleApp1.MyJsonContext.ExpandConverter(Type, JsonConverter, JsonSerializerOptions, Boolean) + 0x50 at ConsoleApp1.MyJsonContext.Create_MyEnum(JsonSerializerOptions) + 0x6d at System.Text.Json.JsonSerializerOptions.GetTypeInfoNoCaching(Type) + 0x47 at System.Text.Json.JsonSerializerOptions.CachingContext.CreateCacheEntry(Type type, JsonSerializerOptions.CachingContext context) + 0x1d --- End of stack trace from previous location --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + 0x20 at System.Text.Json.JsonSerializerOptions.CachingContext.CacheEntry.GetResult() + 0x1e at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type, Boolean, Nullable`1, Boolean, Boolean) + 0x3c at System.Text.Json.JsonSerializerOptions.GetTypeInfo(Type) + 0x4c at ConsoleApp1.MyJsonContext.get_MyEnum() + 0x36 at ConsoleApp1.Program.Main(String[] args) + 0xa6 at ConsoleApp1!<BaseAddress>+0x15c600 </pre> <br /> 바로 이 문제를 개선한 JsonStringEnumConverter의 제네릭 버전이 .NET 8에 추가된 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > JsonStringEnumConverter<TEnum> Class ; <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonstringenumconverter-1'>https://learn.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonstringenumconverter-1</a> </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;' > 1) 필드에 직접 설정하거나, public struct MyPerson { [JsonConverter(typeof(<span style='color: blue; font-weight: bold'>JsonStringEnumConverter<Language></span>))] public Language Language { get; set; } } </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 2) enum 타입에 직접 설정 [JsonConverter(typeof(<span style='color: blue; font-weight: bold'>JsonStringEnumConverter<Language></span>))] public enum Language { CSharp, Cpp, Python, Go } </pre> <br /> 이제 AOT 배포 후에도 잘 실행이 됩니다.<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=2121&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1319
(왼쪽의 숫자를 입력해야 합니다.)