前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >.NET9 Linux AOT Json序列化

.NET9 Linux AOT Json序列化

作者头像
JusterZhu
发布2025-01-23 21:01:26
发布2025-01-23 21:01:26
11000
代码可运行
举报
文章被收录于专栏:JusterZhuJusterZhu
运行总次数:0
代码可运行

1.概要

记录一下流水账在Linux(Ubuntu)操作系统发布AOT程序,然后发现System.Text.Json组件对于Json序列化抛出异常的处理步骤。

2.详细内容

在 .NET 6 和更高版本中,你可以使用 Ahead-of-Time (AOT) 编译来优化应用程序的启动时间和性能。AOT 编译是通过 .NET Native AOT 进行的,尤其适合于需要快速启动和小型部署的场景。要使用 AOT 编译,你需要在项目文件中进行一些配置,并使用命令行工具进行发布。以下是一个简单的步骤来配置和发布 AOT 的示例:

代码语言:javascript
代码运行次数:0
复制
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
    <PublishAot>true</PublishAot>
  </PropertyGroup>
</Project>
代码语言:javascript
代码运行次数:0
复制
//发布命令
dotnet publish -c Release -r linux-x64 --self-contained

问题1,不同操作系统:

如果你是初次发布将会遇到这样的问题。

代码语言:javascript
代码运行次数:0
复制
error : Cross-OS native compilation is not supported.

该错误消息表明你正在尝试在与目标操作系统不同的环境中进行原生编译(AOT)。简单来说,你可能是在 Windows 或 macOS 上尝试为 Linux 进行 AOT 编译,而这种跨操作系统的编译是不被支持的。所以最简单的解决办法就是把代码拷贝到Ubuntu然后再编译即可。

问题2,缺少环境:

当我们把代码拷贝到Ubuntu操作系统上之后,再次敲发布命令编译(这里编译可以用VS Code或Rider)会遇到以下问题。

代码语言:javascript
代码运行次数:0
复制
/home/justerzhu/.nuget/packages/microsoft.dotnet.ilcompiler/8.0.10/build/Microsoft.NETCore.Native.Unix.targets(209,5): error : Platform linker ('clang' or 'gcc') not found in PATH. Ensure you have all the required prerequisites documented at https://aka.ms/nativeaot-prerequisites. [/home/justerzhu/Documents/code/client/GeneralUpdate.Client/GeneralUpdate.Client.csproj]

该错误消息表明在你的 Linux 环境中没有找到平台链接器(clanggcc)。在使用 .NET 的 AOT 编译时,需要使用这些工具来处理本机代码的链接。解决这个问题的步骤如下:

安装必要的工具

你需要确保在 Linux 系统上安装了 clanggcc。以下是如何在常见的 Linux 发行版上安装这些工具的步骤:

Ubuntu

更新软件包列表:

代码语言:javascript
代码运行次数:0
复制
sudo apt update

安装 clanggcc

代码语言:javascript
代码运行次数:0
复制
sudo apt install clang gcc

确保 clanggcc 在你的 PATH 中:

安装完成后,通常它们会自动添加到 PATH 中。可以通过以下命令检查:

代码语言:javascript
代码运行次数:0
复制
clang --version
gcc --version

如果这些命令返回版本信息,则说明安装成功。

检查链接器

确保你的环境能够正确找到链接器。在终端中运行以下命令以确认:

代码语言:javascript
代码运行次数:0
复制
which clang
which gcc

如果这两个命令中的任意一个返回路径,则说明链接器已正确安装并在 PATH 中。

问题3,缺少环境的问题再次来袭:

当我们满怀信心再次敲发布命令的时候,突然又出现了问题。

代码语言:javascript
代码运行次数:0
复制
error : linker command failed with exit code 1 (use -v to see invocation) [/home/justerzhu/Documents/code/client/GeneralUpdate.Client/GeneralUpdate.Client.csproj]
/home/justerzhu/.nuget/packages/microsoft.dotnet.ilcompiler/8.0.10/build/Microsoft.NETCore.Native.targets(366,5): error MSB3073: The command ""clang" "obj/Release/net8.0/linux-x64/native/GeneralUpdate.Client.o" -o "bin/Release/net8.0/linux-x64/native/GeneralUpdate.Client" -Wl,--version-script=obj/Release/net8.0/linux-x64/native/GeneralUpdate.Client.exports -Wl,--export-dynamic -gz=zlib -fuse-ld=bfd /home/justerzhu/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/8.0.10/sdk/libbootstrapper.o /home/justerzhu/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/8.0.10/sdk/libRuntime.WorkstationGC.a /home/justerzhu/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/8.0.10/sdk/libeventpipe-disabled.a /home/justerzhu/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/8.0.10/sdk/libstdc++compat.a /home/justerzhu/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/8.0.10/framework/libSystem.Native.a /home/justerzhu/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/8.0.10/framework/libSystem.Globalization.Native.a /home/justerzhu/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/8.0.10/framework/libSystem.IO.Compression.Native.a /home/justerzhu/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/8.0.10/framework/libSystem.Net.Security.Native.a /home/justerzhu/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/8.0.10/framework/libSystem.Security.Cryptography.Native.OpenSsl.a -g -Wl,-rpath,'$ORIGIN' -Wl,--build-id=sha1 -Wl,--as-needed -pthread -ldl -lz -lrt -lm -pie -Wl,-pie -Wl,-z,relro -Wl,-z,now -Wl,--eh-frame-hdr -Wl,--discard-all -Wl,--gc-sections" exited with code 1. [/home/justerzhu/Documents/code/client/GeneralUpdate.Client/GeneralUpdate.Client.csproj]

该错误表明在链接阶段出现了问题,导致 clang 链接器命令失败并返回了非零退出代码。要解决这个问题,我们需要进一步分析错误原因。以下是一些可能的原因和解决方案:

检查依赖库

缺少依赖库

  • 确保你已经安装了所有必需的开发库。特别是对于 Linux 上的 AOT 编译,可能需要一些额外的库,例如 libc-devlibz-devlibssl-dev 等。
  • 你可以通过以下命令安装常用的开发库: sudo apt install build-essential zlib1g-dev libssl-dev

链接器选项问题

  • 有时错误可能是由于链接器选项不兼容或不正确。检查项目文件或构建配置中是否有自定义的链接器选项,确保它们是正确的。
增加详细输出

增加编译过程的详细输出可以帮助你了解问题的细节:

代码语言:javascript
代码运行次数:0
复制
dotnet publish -c Release -r linux-x64 --self-contained -v diag

-v diag 会输出详细的诊断信息,可以帮助你找到更具体的错误原因。

检查代码中使用的库

如果你的项目中使用了某些特定的 C++ 或本地库,确保它们的头文件和库文件在 Linux 环境中是可用且兼容的。

验证 Native AOT 的支持

确认使用的 .NET Native AOT 工具链版本是正确的,并且你的项目和依赖项支持 AOT 编译。某些第三方库或特性可能不支持 AOT 编译。

使用最新的工具链

确保使用的是最新版本的 .NET SDK 和工具链。你可以通过以下命令更新你的 .NET SDK:

代码语言:javascript
代码运行次数:0
复制
sudo apt update
sudo apt install dotnet-sdk-8.0

问题4,版本不一致:

在Linux发布之前我安装了.NET8的运行环境,但是我项目里应用的是.NET9的nuget这时候就导致了无法正常发布AOT。所以这里也是提醒各位发布之前注意版本对齐。

代码语言:javascript
代码运行次数:0
复制
System.MissingMethodException: Method not found: 'Void System.Text.Json.Serialization.Metadata.JsonObjectInfoValues1<GeneralUpdate.Common.Shared.Object.VersionRespDTO>.set_ConstructorAttributeProviderFactory(System.Func1<System.Reflection.ICustomAttributeProvider>)'.
at Internal.Runtime.TypeLoaderExceptionHelper.CreateMissingMethodException(ExceptionStringID, String) + 0x47
at Internal.Runtime.CompilerHelpers.ThrowHelpers.ThrowMissingMethodException(ExceptionStringID, String) + 0x6
at GeneralUpdate.Common.AOT.JsonContext.VersionRespJsonContext.Create_VersionRespDTO(JsonSerializerOptions) + 0x12
at System.Text.Json.JsonSerializerOptions.GetTypeInfoNoCaching(Type) + 0x43
at System.Text.Json.JsonSerializerOptions.CachingContext.CreateCacheEntry(Type type, JsonSerializerOptions.CachingContext context) + 0x1e
--- End of stack trace from previous location ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + 0x1c
at System.Text.Json.JsonSerializerOptions.CachingContext.CacheEntry.GetResult() + 0x1b
at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type, Boolean, Nullable`1, Boolean, Boolean) + 0x44
at System.Text.Json.JsonSerializerOptions.GetTypeInfo(Type) + 0x49
at GeneralUpdate.Common.AOT.JsonContext.VersionRespJsonContext.get_VersionRespDTO() + 0x39
at GeneralUpdate.Common.Shared.Service.VersionService.<Validate>d__2.MoveNext() + 0x114
--- End of stack trace from previous location ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + 0x1c
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task) + 0xbe
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task, ConfigureAwaitOptions) + 0x4e
at GeneralUpdate.ClientCore.GeneralClientBootstrap.<ExecuteWorkflowAsync>d__13.MoveNext() + 0x109

这个时候只能在电脑上把nuget的版本一个个对齐或者安装对应.NET的版本即可。

问题5,Json序列化怎么转都报错:

具体的报错我没有记录,但是不处理Json序列化一定会报错。下面这段代码在非AOT发布的情况是可以正常使用的,但是在AOT发布时存在问题(原因在参考资料中了解)。

代码语言:javascript
代码运行次数:0
复制
 var jsonStr = JsonSerializer.Serialize(parameters);

参考资料:

  • https://devblogs.microsoft.com/dotnet/system-text-json-in-dotnet-8/
  • https://learn.microsoft.com/zh-cn/dotnet/standard/serialization/system-text-json/source-generation

这时候只能找找参考资料,看完一遍之后大致写法如下 :

代码语言:javascript
代码运行次数:0
复制
class Packet
{
    [JsonPropertyName("Name")]
    public string? Name { get; set; }

    [JsonPropertyName("Hash")]
    public string Hash { get; set; }
    
    //....
}

[JsonSerializable(typeof(Packet))]
public partial class PacketJsonContext : JsonSerializerContext;


 var jsonStr = JsonSerializer.Serialize(parameters,PacketJsonContext.Default.Packet);

以上写法规避了Json在AOT中使用报错的问题,但是[JsonSerializable(typeof(Packet))]中typeof的对象可不一定是都是自定义对象那怎么写?

集合类型:

代码语言:javascript
代码运行次数:0
复制
[JsonSerializable(typeof(List<FileNode>))]
public partial class FileNodesJsonContext : JsonSerializerContext;

字典类型,基础类型:

代码语言:javascript
代码运行次数:0
复制
[JsonSerializable(typeof(bool))]
[JsonSerializable(typeof(bool?))]
[JsonSerializable(typeof(int))]
[JsonSerializable(typeof(int?))]
[JsonSerializable(typeof(string))]
[JsonSerializable(typeof(Dictionary<string, object>))]
public partial class HttpParameterJsonContext: JsonSerializerContext;

这里大家可以看到我写了一堆JsonSerializable,为什么?只写了bool、int、string为什么?

一句话回答是因为,在定义[JsonSerializable(typeof(Dictionary<string, object>))]时候包含了object类型,而写了那么多类型是因为object只有这几种数据类型的可能。所以可能被object对象装载的对象都要标记出来。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-12-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JusterZhu 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.概要
  • 2.详细内容
    • 问题1,不同操作系统:
    • 问题2,缺少环境:
      • 安装必要的工具
      • Ubuntu
      • 检查链接器
    • 问题3,缺少环境的问题再次来袭:
      • 检查依赖库
      • 增加详细输出
      • 检查代码中使用的库
      • 验证 Native AOT 的支持
      • 使用最新的工具链
    • 问题4,版本不一致:
    • 问题5,Json序列化怎么转都报错:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档