C#(.NET)으로 만든 앱을 네이티브 AOT로 게시하면 자체 포함 배포처럼 네이티브 코드로 AOT(ahead-of-time) 컴파일된 앱이 생성된다. 즉 IL(Intermediate language)을 네이티브 코드로 컴파일한다.1
System.Text.Json에서 원본을 생성하는 방법은 몇 가지 추가 및 제약 사항이 있는데 아래의 예제는 이를 설명하는 소스이다.2 테스트를 위해 생성한 파일은 Program.cs, TestModel.cs, JsonHelper.cs
이다.
테스트를 위하여 콘솔 프로젝트를 만들고 프로젝트 설정(~.csproj)을 아래와 같이 변경하고 터미널에서 dotnet publish -c Release -r win-x64
빌드한다. 정상적으로 빌드가 완료되면 하위 폴더 Native
에 실행파일이 생성된다.
프로젝트 설정(변경)
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<PublishAot>true</PublishAot>
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>
</Project>
TestModel.cs
using System.Text.Json.Serialization;
namespace ConsoleTest;
public class TestModel : IC
{
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string FullName { get; set; } = string.Empty;
// [JsonConverter(typeof(JsonStringEnumConverter<ETest>))]
public ETest ETest { get; set; } = 0;
public string CodeName { get; set; } = "CodeName";
}
public interface IA
{
[JsonPropertyOrder(1)]
string FirstName { get; set; }
}
public interface IB : IA
{
[JsonPropertyOrder(2)]
string LastName { get; set; }
}
public interface IC : IB
{
[JsonPropertyOrder(3)]
string FullName { get; set; }
[JsonPropertyOrder(4)]
ETest ETest { get; set; }
[JsonIgnore]
string CodeName { get; set; }
}
[JsonConverter(typeof(JsonStringEnumConverter<ETest>))]
public enum ETest
{
Cpp,
Python,
Go
}
// [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Serialization)]
// [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(IA))]
[JsonSerializable(typeof(IB))]
[JsonSerializable(typeof(IC))]
[JsonSerializable(typeof(TestModel))]
//[JsonSerializable(typeof(bool))] //class 프로퍼티가 object type일 경우 데이터 타입 명시
//[JsonSerializable(typeof(int))]
public partial class TestModelContext : JsonSerializerContext;
public static class AotMessage
{
public const string AOT = "Need runtime code generation for native AOT(may break when trimming)";
}
JsonHelper.cs
using System;
using System.Diagnostics.CodeAnalysis;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
namespace ConsoleTest;
public static class JsonHelper
{
private static JsonSerializerOptions Options;
static JsonHelper()
{
Options = new JsonSerializerOptions();
}
[RequiresUnreferencedCode(AotMessage.AOT)]
[RequiresDynamicCode(AotMessage.AOT)]
public static string ModelToJson<T>(T modelClass, IJsonTypeInfoResolver resolver)
{
try
{
if (typeof(T).Name == "String")
{
return "string.Empty1";
}
if (!(typeof(T).IsInterface || typeof(T).IsClass))
{
return "string.Empty2";
}
Options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = null,
TypeInfoResolver = resolver
};
string _result = JsonSerializer.Serialize(modelClass, Options);
return string.IsNullOrWhiteSpace(_result) ? string.Empty : _result;
}
catch (Exception _ex)
{
return _ex.Message;
}
}
[RequiresUnreferencedCode(AotMessage.AOT)]
[RequiresDynamicCode(AotMessage.AOT)]
public static T JsonToModel<T>(string jsonString, IJsonTypeInfoResolver resolver) where T : class, new()
{
try
{
Options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = null,
TypeInfoResolver = resolver
};
T? _result = JsonSerializer.Deserialize<T>(jsonString, Options);
return _result ?? new T();
}
catch
{
return new T();
}
}
}
Program.cs
using System;
using System.Diagnostics.CodeAnalysis;
namespace ConsoleTest;
//Use concrete types when possible for improved performance
#pragma warning disable CA1859
internal class Program
{
[RequiresUnreferencedCode(AotMessage.AOT)]
[RequiresDynamicCode(AotMessage.AOT)]
private static void Main()
{
IA _ia = new TestModel();
IB _ib = new TestModel();
IC _ic = new TestModel();
_ia.FirstName = "a1가";
_ib.FirstName = "b1";
_ib.LastName = "b2";
_ic.FirstName = "c1";
_ic.LastName = "c2";
_ic.FullName = "c3";
_ic.ETest = ETest.Python;
TestModelContext _context = new();
string _result = JsonHelper.ModelToJson(_ia, _context);
Console.WriteLine($"{_result}");
IA _a = JsonHelper.JsonToModel<TestModel>(_result, _context);
Console.WriteLine($"{_a.FirstName}");
Console.WriteLine("--------------------------------------------------");
_result = JsonHelper.ModelToJson(_ib, _context);
Console.WriteLine($"{_result}");
IB _b = JsonHelper.JsonToModel<TestModel>(_result, _context);
Console.WriteLine($"{_b.FirstName} {_b.LastName}");
Console.WriteLine("--------------------------------------------------");
_result = JsonHelper.ModelToJson(_ic, _context);
Console.WriteLine($"{_result}");
IC _c = JsonHelper.JsonToModel<TestModel>(_result, _context);
Console.WriteLine($"{_c.FirstName} {_c.LastName} {_c.FullName} {_c.ETest} {_c.CodeName}");
}
#pragma warning restore CA1859
/*
{"FirstName":"a1가"}
a1가
--------------------------------------------------
{"FirstName":"b1","LastName":"b2"}
b1 b2
--------------------------------------------------
{"FirstName":"c1","LastName":"c2","FullName":"c3","ETest":"Python"}
c1 c2 c3 Python CodeName
*/
}