diff --git a/Kook.Net.slnx b/Kook.Net.slnx
index 43122329..3dd2fb36 100644
--- a/Kook.Net.slnx
+++ b/Kook.Net.slnx
@@ -19,6 +19,7 @@
+
diff --git a/props/common.props b/props/common.props
index fe9aa6eb..b83f1109 100644
--- a/props/common.props
+++ b/props/common.props
@@ -5,8 +5,6 @@
0.10.4
false
- false
- false
latest
true
snupkg
@@ -31,6 +29,11 @@
true
pdbonly
+
+
+ true
+ true
+
$(VersionPrefix)
$(VersionPrefix)-$(VersionSuffix)
diff --git a/samples/Kook.Net.Samples.NativeAOT/Kook.Net.Samples.NativeAOT.csproj b/samples/Kook.Net.Samples.NativeAOT/Kook.Net.Samples.NativeAOT.csproj
new file mode 100644
index 00000000..e1581827
--- /dev/null
+++ b/samples/Kook.Net.Samples.NativeAOT/Kook.Net.Samples.NativeAOT.csproj
@@ -0,0 +1,20 @@
+
+
+
+
+
+ win-x64
+ true
+ false
+ true
+ true
+ full
+ Speed
+ false
+
+
+
+
+
+
+
diff --git a/samples/Kook.Net.Samples.NativeAOT/Program.cs b/samples/Kook.Net.Samples.NativeAOT/Program.cs
new file mode 100644
index 00000000..a4c368e0
--- /dev/null
+++ b/samples/Kook.Net.Samples.NativeAOT/Program.cs
@@ -0,0 +1,86 @@
+using Kook;
+using Kook.WebSocket;
+
+// Kook.Net NativeAOT 示例
+// 此示例演示如何在 NativeAOT 编译模式下使用 Kook.Net
+
+Console.WriteLine("Kook.Net NativeAOT Sample");
+Console.WriteLine("========================");
+Console.WriteLine();
+
+// 从环境变量或配置文件读取 Token
+string? token = Environment.GetEnvironmentVariable("KOOK_TOKEN");
+if (string.IsNullOrEmpty(token))
+{
+ Console.WriteLine("Error: KOOK_TOKEN environment variable is not set.");
+ Console.WriteLine("Please set your bot token:");
+ Console.WriteLine(" export KOOK_TOKEN=\"your-bot-token-here\"");
+ return 1;
+}
+
+// 创建客户端配置
+// 注意:NativeAOT 不支持 Kook.Net.Commands 框架,因为它依赖反射
+KookSocketConfig config = new()
+{
+ AlwaysDownloadUsers = false,
+ MessageCacheSize = 100,
+ LogLevel = LogSeverity.Info,
+ StartupCacheFetchMode = StartupCacheFetchMode.Synchronous
+};
+
+// 创建客户端
+using KookSocketClient client = new(config);
+
+// 设置事件处理器
+client.Log += LogAsync;
+client.Ready += ReadyAsync;
+client.MessageReceived += MessageReceivedAsync;
+
+// 登录并启动
+try
+{
+ await client.LoginAsync(TokenType.Bot, token);
+ await client.StartAsync();
+
+ Console.WriteLine("Bot is running. Press Ctrl+C to exit.");
+
+ // 保持程序运行
+ await Task.Delay(Timeout.Infinite);
+}
+catch (Exception ex)
+{
+ Console.WriteLine($"Error: {ex.Message}");
+ return 1;
+}
+
+return 0;
+
+static Task LogAsync(LogMessage msg)
+{
+ Console.WriteLine($"[{msg.Severity}] {msg.Source}: {msg.Message}");
+ if (msg.Exception != null)
+ Console.WriteLine($" Exception: {msg.Exception}");
+ return Task.CompletedTask;
+}
+
+static Task ReadyAsync()
+{
+ Console.WriteLine("Bot is ready!");
+ return Task.CompletedTask;
+}
+
+static Task MessageReceivedAsync(SocketMessage message, SocketGuildUser user, SocketTextChannel channel)
+{
+ // 忽略系统消息和 Bot 自己的消息
+ if (message.Author.IsBot == true || message.Author.IsSystemUser)
+ return Task.CompletedTask;
+
+ // 简单的 ping 命令
+ if (message.Content.Equals("!ping", StringComparison.OrdinalIgnoreCase))
+ {
+ // 在 NativeAOT 模式下,直接回复消息
+ _ = message.Channel.SendTextAsync("Pong! 🏓");
+ }
+
+ return Task.CompletedTask;
+}
diff --git a/samples/Kook.Net.Samples.NativeAOT/README.md b/samples/Kook.Net.Samples.NativeAOT/README.md
new file mode 100644
index 00000000..ab162a05
--- /dev/null
+++ b/samples/Kook.Net.Samples.NativeAOT/README.md
@@ -0,0 +1,59 @@
+# Kook.Net NativeAOT 示例
+
+此示例演示如何在 NativeAOT 编译模式下使用 Kook.Net。
+
+## 功能特性
+
+- ✅ REST API 调用(使用 JSON 源生成)
+- ✅ WebSocket 连接
+- ✅ 事件处理
+- ✅ 消息发送和接收
+- ❌ Commands 框架(不支持,因为依赖反射)
+
+## 运行示例
+
+### 开发模式运行
+
+```bash
+export KOOK_TOKEN="your-bot-token-here"
+dotnet run
+```
+
+### 发布为 NativeAOT
+
+```bash
+dotnet publish -c Release
+
+# 运行已编译的原生可执行文件
+export KOOK_TOKEN="your-bot-token-here"
+./bin/Release/net8.0/linux-x64/publish/Kook.Net.Samples.NativeAOT
+```
+
+## NativeAOT 限制
+
+1. **Commands 框架不可用**: `Kook.Net.Commands` 使用反射进行命令发现和参数绑定,这与 NativeAOT 不兼容。
+ - 解决方案:手动处理消息并实现命令逻辑
+
+2. **JSON 序列化**: Kook.Net 使用源生成的 `KookJsonSerializerContext` 来支持 NativeAOT。
+ - 所有 API 模型都已注册用于源生成
+ - 自定义类型可能需要额外的序列化配置
+
+3. **程序集大小**: NativeAOT 编译的可执行文件会比常规 .NET 应用程序大,但启动速度更快,内存占用更低。
+
+## 测试 Bot
+
+运行 Bot 后,在 Kook 频道中发送 `!ping` 命令,Bot 会回复 "Pong! 🏓"。
+
+## 性能优势
+
+NativeAOT 编译的应用程序具有以下优势:
+
+- **快速启动**: 无需 JIT 编译
+- **较低内存占用**: 无需加载整个 .NET 运行时
+- **单文件部署**: 可执行文件包含所有依赖项
+- **更好的代码保护**: 原生代码更难反编译
+
+## 相关资源
+
+- [.NET NativeAOT 文档](https://learn.microsoft.com/dotnet/core/deploying/native-aot/)
+- [Kook.Net 文档](https://kooknet.dev/)
diff --git a/samples/Kook.Net.Samples.SimpleBot/Program.cs b/samples/Kook.Net.Samples.SimpleBot/Program.cs
index cb26d750..a3fafadc 100644
--- a/samples/Kook.Net.Samples.SimpleBot/Program.cs
+++ b/samples/Kook.Net.Samples.SimpleBot/Program.cs
@@ -1,5 +1,4 @@
using System.Diagnostics;
-using System.Text.Json;
using Kook;
using Kook.Rest;
using Kook.WebSocket;
diff --git a/src/Kook.Net.Commands/Kook.Net.Commands.csproj b/src/Kook.Net.Commands/Kook.Net.Commands.csproj
index 5503dfd7..83b8ce35 100644
--- a/src/Kook.Net.Commands/Kook.Net.Commands.csproj
+++ b/src/Kook.Net.Commands/Kook.Net.Commands.csproj
@@ -5,6 +5,9 @@
true
Kook.Net.Commands
The text message based command framework extension for Kook.Net.
+
+ false
+ false
diff --git a/src/Kook.Net.Commands/Readers/NullableTypeReader.cs b/src/Kook.Net.Commands/Readers/NullableTypeReader.cs
index 949e85ce..d8ed179c 100644
--- a/src/Kook.Net.Commands/Readers/NullableTypeReader.cs
+++ b/src/Kook.Net.Commands/Readers/NullableTypeReader.cs
@@ -1,16 +1,50 @@
+using System.Collections.Frozen;
+using System.Diagnostics.CodeAnalysis;
using System.Reflection;
namespace Kook.Commands;
internal static class NullableTypeReader
{
+ private static readonly FrozenDictionary> PrimitiveNullableReaders =
+ new Dictionary>
+ {
+ [typeof(bool)] = x => new NullableTypeReader(x),
+ [typeof(byte)] = x => new NullableTypeReader(x),
+ [typeof(sbyte)] = x => new NullableTypeReader(x),
+ [typeof(short)] = x => new NullableTypeReader(x),
+ [typeof(ushort)] = x => new NullableTypeReader(x),
+ [typeof(int)] = x => new NullableTypeReader(x),
+ [typeof(uint)] = x => new NullableTypeReader(x),
+ [typeof(long)] = x => new NullableTypeReader(x),
+ [typeof(ulong)] = x => new NullableTypeReader(x),
+ [typeof(float)] = x => new NullableTypeReader(x),
+ [typeof(double)] = x => new NullableTypeReader(x),
+ [typeof(decimal)] = x => new NullableTypeReader(x),
+ [typeof(DateTime)] = x => new NullableTypeReader(x),
+ [typeof(DateTimeOffset)] = x => new NullableTypeReader(x),
+ [typeof(Guid)] = x => new NullableTypeReader(x),
+ [typeof(TimeSpan)] = x => new NullableTypeReader(x),
+#if NET6_0_OR_GREATER
+ [typeof(DateOnly)] = x => new NullableTypeReader(x),
+ [typeof(TimeOnly)] = x => new NullableTypeReader(x),
+#endif
+ [typeof(char)] = x => new NullableTypeReader(x),
+ }
+ .ToFrozenDictionary();
+
public static TypeReader Create(Type type, TypeReader reader)
{
+ if (PrimitiveNullableReaders.TryGetValue(type, out Func? factory))
+ return factory(reader);
ConstructorInfo constructor = typeof(NullableTypeReader<>).MakeGenericType(type).GetTypeInfo().DeclaredConstructors.First();
return (TypeReader)constructor.Invoke([reader]);
}
}
+#if NET6_0_OR_GREATER
+[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
+#endif
internal class NullableTypeReader : TypeReader
where T : struct
{
@@ -26,7 +60,7 @@ public override async Task ReadAsync(ICommandContext context,
{
if (string.Equals(input, "null", StringComparison.OrdinalIgnoreCase)
|| string.Equals(input, "nothing", StringComparison.OrdinalIgnoreCase))
- return TypeReaderResult.FromSuccess(new T());
+ return TypeReaderResult.FromSuccess(new T( ));
return await _baseTypeReader.ReadAsync(context, input, services).ConfigureAwait(false);
}
}
diff --git a/src/Kook.Net.Commands/Readers/PrimitiveTypeReader.cs b/src/Kook.Net.Commands/Readers/PrimitiveTypeReader.cs
index fe42a25f..c14db4f2 100644
--- a/src/Kook.Net.Commands/Readers/PrimitiveTypeReader.cs
+++ b/src/Kook.Net.Commands/Readers/PrimitiveTypeReader.cs
@@ -1,14 +1,48 @@
+using System.Collections.Frozen;
+using System.Diagnostics.CodeAnalysis;
+
namespace Kook.Commands;
internal static class PrimitiveTypeReader
{
+ private static readonly FrozenDictionary> PrimitiveReaders =
+ new Dictionary>
+ {
+ [typeof(bool)] = () => new PrimitiveTypeReader(),
+ [typeof(byte)] = () => new PrimitiveTypeReader(),
+ [typeof(sbyte)] = () => new PrimitiveTypeReader(),
+ [typeof(short)] = () => new PrimitiveTypeReader(),
+ [typeof(ushort)] = () => new PrimitiveTypeReader(),
+ [typeof(int)] = () => new PrimitiveTypeReader(),
+ [typeof(uint)] = () => new PrimitiveTypeReader(),
+ [typeof(long)] = () => new PrimitiveTypeReader(),
+ [typeof(ulong)] = () => new PrimitiveTypeReader(),
+ [typeof(float)] = () => new PrimitiveTypeReader(),
+ [typeof(double)] = () => new PrimitiveTypeReader(),
+ [typeof(decimal)] = () => new PrimitiveTypeReader(),
+ [typeof(DateTime)] = () => new PrimitiveTypeReader(),
+ [typeof(DateTimeOffset)] = () => new PrimitiveTypeReader(),
+ [typeof(Guid)] = () => new PrimitiveTypeReader(),
+#if NET6_0_OR_GREATER
+ [typeof(DateOnly)] = () => new PrimitiveTypeReader(),
+ [typeof(TimeOnly)] = () => new PrimitiveTypeReader(),
+#endif
+ [typeof(char)] = () => new PrimitiveTypeReader(),
+ }
+ .ToFrozenDictionary();
+
public static TypeReader? Create(Type type)
{
+ if (PrimitiveReaders.TryGetValue(type, out Func? factory))
+ return factory();
type = typeof(PrimitiveTypeReader<>).MakeGenericType(type);
return Activator.CreateInstance(type) as TypeReader;
}
}
+#if NET6_0_OR_GREATER
+[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
+#endif
internal class PrimitiveTypeReader : TypeReader
{
private readonly TryParseDelegate _tryParse;
diff --git a/src/Kook.Net.Core/Entities/Guilds/GuildFeatures.cs b/src/Kook.Net.Core/Entities/Guilds/GuildFeatures.cs
index ca1688ef..452a856e 100644
--- a/src/Kook.Net.Core/Entities/Guilds/GuildFeatures.cs
+++ b/src/Kook.Net.Core/Entities/Guilds/GuildFeatures.cs
@@ -61,7 +61,11 @@ internal void EnsureFeature(GuildFeature feature)
{
if (HasFeature(feature)) return;
GuildFeatures features = this;
+#if NET8_0_OR_GREATER
+ IEnumerable values = Enum.GetValues();
+#else
IEnumerable values = Enum.GetValues(typeof(GuildFeature)).Cast();
+#endif
IEnumerable missingValues = values.Where(x => feature.HasFlag(x) && !features.Value.HasFlag(x)).ToList();
throw new InvalidOperationException($"Missing required guild feature{(missingValues.Count() > 1 ? "s" : "")} {string.Join(", ", missingValues.Select(x => x.ToString()))} in order to execute this operation.");
}
diff --git a/src/Kook.Net.Core/Entities/Messages/Embeds/NotImplementedEmbed.cs b/src/Kook.Net.Core/Entities/Messages/Embeds/NotImplementedEmbed.cs
index b5893aee..627b16f9 100644
--- a/src/Kook.Net.Core/Entities/Messages/Embeds/NotImplementedEmbed.cs
+++ b/src/Kook.Net.Core/Entities/Messages/Embeds/NotImplementedEmbed.cs
@@ -1,7 +1,9 @@
+using System.Diagnostics.CodeAnalysis;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
+using System.Text.Json.Serialization.Metadata;
namespace Kook;
@@ -35,6 +37,10 @@ internal NotImplementedEmbed(string rawType, JsonNode jsonNode)
/// 用于反序列化操作的选项。
/// 要解析为的具体类型。
/// 解析后的嵌入式内容。
+#if NET5_0_OR_GREATER
+ [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a resolving function for AOT applications.")]
+ [RequiresDynamicCode("JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use the overload that takes a resolving function for AOT applications.")]
+#endif
public T? Resolve(JsonSerializerOptions? options = null)
where T : IEmbed
{
@@ -47,6 +53,34 @@ internal NotImplementedEmbed(string rawType, JsonNode jsonNode)
return embed;
}
+ ///
+ /// 通过 JSON 反序列化将嵌入式内容解析为具体类型。
+ ///
+ /// 用于反序列化操作的类型信息。
+ /// 要解析为的具体类型。
+ /// 解析后的嵌入式内容。
+ public T? Resolve(JsonTypeInfo jsonTypeInfo)
+ where T : IEmbed
+ {
+ if (jsonTypeInfo is not JsonTypeInfo typedJsonTypeInfo)
+ throw new ArgumentException($"The provided JsonTypeInfo is not of the expected type {typeof(T).FullName}.", nameof(jsonTypeInfo));
+ T? embed = JsonNode.Deserialize(typedJsonTypeInfo);
+ return embed;
+ }
+
+ ///
+ /// 通过 JSON 反序列化将嵌入式内容解析为具体类型。
+ ///
+ /// 用于反序列化操作的类型信息。
+ /// 要解析为的具体类型。
+ /// 解析后的嵌入式内容。
+ public T? Resolve(JsonTypeInfo jsonTypeInfo)
+ where T : IEmbed
+ {
+ T? embed = JsonNode.Deserialize(jsonTypeInfo);
+ return embed;
+ }
+
///
/// 通过指定的解析函数将嵌入式内容解析为具体类型。
///
diff --git a/src/Kook.Net.Core/Entities/Messages/Pokes/PokeResources/NotImplementedPokeResource.cs b/src/Kook.Net.Core/Entities/Messages/Pokes/PokeResources/NotImplementedPokeResource.cs
index 649db085..fda728e4 100644
--- a/src/Kook.Net.Core/Entities/Messages/Pokes/PokeResources/NotImplementedPokeResource.cs
+++ b/src/Kook.Net.Core/Entities/Messages/Pokes/PokeResources/NotImplementedPokeResource.cs
@@ -1,7 +1,9 @@
+using System.Diagnostics.CodeAnalysis;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
+using System.Text.Json.Serialization.Metadata;
namespace Kook;
@@ -35,6 +37,10 @@ internal NotImplementedPokeResource(string rawType, JsonNode jsonNode)
/// 用于反序列化操作的选项。
/// 要解析为的具体类型。
/// 解析后的 POKE 资源。
+#if NET5_0_OR_GREATER
+ [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a resolving function for AOT applications.")]
+ [RequiresDynamicCode("JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use the overload that takes a resolving function for AOT applications.")]
+#endif
public T? Resolve(JsonSerializerOptions? options = null)
where T : IPokeResource
{
@@ -47,6 +53,34 @@ internal NotImplementedPokeResource(string rawType, JsonNode jsonNode)
return pokeResource;
}
+ ///
+ /// 通过 JSON 反序列化将 POKE 资源解析为具体类型。
+ ///
+ /// 用于反序列化操作的类型信息。
+ /// 要解析为的具体类型。
+ /// 解析后的 POKE 资源。
+ public T? Resolve(JsonTypeInfo jsonTypeInfo)
+ where T : IPokeResource
+ {
+ if (jsonTypeInfo is not JsonTypeInfo typedJsonTypeInfo)
+ throw new ArgumentException($"The provided JsonTypeInfo is not of the expected type {typeof(T).FullName}.", nameof(jsonTypeInfo));
+ T? pokeResource = JsonNode.Deserialize(typedJsonTypeInfo);
+ return pokeResource;
+ }
+
+ ///
+ /// 通过 JSON 反序列化将 POKE 资源解析为具体类型。
+ ///
+ /// 用于反序列化操作的类型信息。
+ /// 要解析为的具体类型。
+ /// 解析后的 POKE 资源。
+ public T? Resolve(JsonTypeInfo jsonTypeInfo)
+ where T : IPokeResource
+ {
+ T? pokeResource = JsonNode.Deserialize(jsonTypeInfo);
+ return pokeResource;
+ }
+
///
/// 通过指定的解析函数将 POKE 资源 解析为具体类型。
///
diff --git a/src/Kook.Net.Core/Entities/Permissions/GuildPermissions.cs b/src/Kook.Net.Core/Entities/Permissions/GuildPermissions.cs
index 594c7e17..81500ecf 100644
--- a/src/Kook.Net.Core/Entities/Permissions/GuildPermissions.cs
+++ b/src/Kook.Net.Core/Entities/Permissions/GuildPermissions.cs
@@ -435,9 +435,13 @@ internal void Ensure(GuildPermission permissions)
{
if (!Has(permissions))
{
+#if NET8_0_OR_GREATER
+ IEnumerable vals = Enum.GetValues();
+#else
IEnumerable vals = Enum
.GetValues(typeof(GuildPermission))
.Cast();
+#endif
ulong currentValues = RawValue;
IEnumerable missingValues = vals
.Where(x => permissions.HasFlag(x) && !Permissions.GetValue(currentValues, x))
diff --git a/src/Kook.Net.Core/Utils/Cacheable.cs b/src/Kook.Net.Core/Utils/Cacheable.cs
index 3e145e6a..05e62b46 100644
--- a/src/Kook.Net.Core/Utils/Cacheable.cs
+++ b/src/Kook.Net.Core/Utils/Cacheable.cs
@@ -1,6 +1,4 @@
-using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
-using System.Reflection;
namespace Kook;
@@ -9,9 +7,6 @@ namespace Kook;
///
/// 可延迟加载的缓存实体的类型。
/// 可延迟加载的缓存实体的 ID 的类型。
-#if DEBUG
-[DebuggerDisplay("{DebuggerDisplay,nq}")]
-#endif
public readonly struct Cacheable
where TEntity : IEntity
where TId : IEquatable
@@ -70,11 +65,6 @@ public Cacheable(TEntity? value, TId id, bool hasValue, Func> dow
///
public async Task GetOrDownloadAsync() => HasValue ? Value : await DownloadAsync().ConfigureAwait(false);
-#if DEBUG
- private string DebuggerDisplay => HasValue && Value != null
- ? $"{Value.GetType().GetProperty("DebuggerDisplay", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(Value) ?? Value.ToString()} (Cacheable)"
- : $"{Id} (Cacheable, {typeof(TEntity).Name})";
-#endif
}
///
@@ -84,9 +74,6 @@ public Cacheable(TEntity? value, TId id, bool hasValue, Func> dow
/// 可从 API 请求下载的实体的类型。
/// 由 和 共同继承或实现的类型。
/// 可延迟加载的缓存实体的 ID 的类型。
-#if DEBUG
-[DebuggerDisplay("{DebuggerDisplay,nq}")]
-#endif
public readonly struct Cacheable
where TCachedEntity : IEntity, TRelationship
where TDownloadableEntity : IEntity, TRelationship
@@ -145,9 +132,4 @@ public Cacheable(TCachedEntity? value, TId id, bool hasValue, Func
public async Task GetOrDownloadAsync() => HasValue ? Value : await DownloadAsync().ConfigureAwait(false);
-#if DEBUG
- private string DebuggerDisplay => HasValue && Value != null
- ? $"{Value.GetType().GetProperty("DebuggerDisplay", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(Value) ?? Value.ToString()} (Cacheable)"
- : $"{Id} (Cacheable, {typeof(TRelationship).Name})";
-#endif
}
diff --git a/src/Kook.Net.DependencyInjection.Microsoft/Configurators/IKookClientServiceConfigurator.cs b/src/Kook.Net.DependencyInjection.Microsoft/Configurators/IKookClientServiceConfigurator.cs
index 963b41b3..eb3f5131 100644
--- a/src/Kook.Net.DependencyInjection.Microsoft/Configurators/IKookClientServiceConfigurator.cs
+++ b/src/Kook.Net.DependencyInjection.Microsoft/Configurators/IKookClientServiceConfigurator.cs
@@ -1,4 +1,5 @@
-using Kook.Rest;
+using System.Diagnostics.CodeAnalysis;
+using Kook.Rest;
using Kook.Webhook;
using Kook.WebSocket;
using Microsoft.Extensions.Options;
@@ -32,7 +33,8 @@ public interface IKookClientServiceConfigurator : IKookClientConfiguratorComplet
/// 客户端的类型。
/// 配置的类型。
/// 配置了基于 Webhook 的网关客户端的配置器。
- IKookClientConfigurator UseWebhookClient(
+ IKookClientConfigurator UseWebhookClient(
Func, TClient> clientFactory, Action configure)
where TClient : KookWebhookClient
where TConfig : KookWebhookConfig;
diff --git a/src/Kook.Net.DependencyInjection.Microsoft/Configurators/KookClientServiceConfigurator.cs b/src/Kook.Net.DependencyInjection.Microsoft/Configurators/KookClientServiceConfigurator.cs
index cef84f7e..37b965ac 100644
--- a/src/Kook.Net.DependencyInjection.Microsoft/Configurators/KookClientServiceConfigurator.cs
+++ b/src/Kook.Net.DependencyInjection.Microsoft/Configurators/KookClientServiceConfigurator.cs
@@ -1,4 +1,5 @@
-using Kook.Rest;
+using System.Diagnostics.CodeAnalysis;
+using Kook.Rest;
using Kook.Webhook;
using Kook.WebSocket;
using Microsoft.Extensions.DependencyInjection;
@@ -36,7 +37,8 @@ public IKookClientConfigurator UseSocketClie
}
///
- public IKookClientConfigurator UseWebhookClient(
+ public IKookClientConfigurator UseWebhookClient(
Func, TClient> clientFactory, Action configure)
where TClient : KookWebhookClient
where TConfig : KookWebhookConfig
diff --git a/src/Kook.Net.DependencyInjection.Microsoft/Configurators/KookWebhookClientConfigurator.cs b/src/Kook.Net.DependencyInjection.Microsoft/Configurators/KookWebhookClientConfigurator.cs
index 1a872391..de800030 100644
--- a/src/Kook.Net.DependencyInjection.Microsoft/Configurators/KookWebhookClientConfigurator.cs
+++ b/src/Kook.Net.DependencyInjection.Microsoft/Configurators/KookWebhookClientConfigurator.cs
@@ -1,10 +1,13 @@
-using Kook.Webhook;
+using System.Diagnostics.CodeAnalysis;
+using Kook.Webhook;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Kook.Net.DependencyInjection.Microsoft;
-internal class KookWebhookClientConfigurator : KookClientConfigurator
+internal class KookWebhookClientConfigurator :
+ KookClientConfigurator
where TClient : KookWebhookClient
where TConfig : KookWebhookConfig
{
diff --git a/src/Kook.Net.DependencyInjection.Microsoft/KookClientDependencyInjectionExtensions.Keyed.cs b/src/Kook.Net.DependencyInjection.Microsoft/KookClientDependencyInjectionExtensions.Keyed.cs
index a0b088b1..7827664d 100644
--- a/src/Kook.Net.DependencyInjection.Microsoft/KookClientDependencyInjectionExtensions.Keyed.cs
+++ b/src/Kook.Net.DependencyInjection.Microsoft/KookClientDependencyInjectionExtensions.Keyed.cs
@@ -1,4 +1,5 @@
-using Kook.Rest;
+using System.Diagnostics.CodeAnalysis;
+using Kook.Rest;
using Kook.Webhook;
using Kook.WebSocket;
using Microsoft.Extensions.DependencyInjection;
@@ -147,13 +148,15 @@ public static IServiceCollection AddKeyedKookWebhookClient(thi
/// Webhook 客户端的类型。
/// Webhook 客户端的配置类型。
/// 添加了 KOOK Webhook 客户端的服务集合。
- public static IServiceCollection AddKeyedKookWebhookClient(this IServiceCollection services,
+ public static IServiceCollection AddKeyedKookWebhookClient(this IServiceCollection services,
string? serviceKey, Func clientFactory)
where TClient: KookWebhookClient
where TConfig: KookWebhookConfig
{
- services.AddKeyedSingleton(serviceKey, (provider, _) => clientFactory(provider, provider.GetRequiredService>().Get(serviceKey)));
+ services.AddKeyedSingleton(serviceKey, (provider, _) =>
+ clientFactory(provider, provider.GetRequiredService>().Get(serviceKey)));
services.AddKeyedKookWebhookClient(serviceKey);
return services;
}
diff --git a/src/Kook.Net.DependencyInjection.Microsoft/KookClientDependencyInjectionExtensions.cs b/src/Kook.Net.DependencyInjection.Microsoft/KookClientDependencyInjectionExtensions.cs
index 22348163..807ae628 100644
--- a/src/Kook.Net.DependencyInjection.Microsoft/KookClientDependencyInjectionExtensions.cs
+++ b/src/Kook.Net.DependencyInjection.Microsoft/KookClientDependencyInjectionExtensions.cs
@@ -1,4 +1,5 @@
-using Kook.Rest;
+using System.Diagnostics.CodeAnalysis;
+using Kook.Rest;
using Kook.Webhook;
using Kook.WebSocket;
using Microsoft.Extensions.DependencyInjection;
@@ -120,10 +121,12 @@ public static IServiceCollection AddKookSocketClient(this IServiceCollection ser
/// Webhook 客户端的类型。
/// Webhook 客户端的配置类型。
/// 添加了 KOOK Webhook 客户端的服务集合。
- public static IServiceCollection AddKookWebhookClient(this IServiceCollection services,
+ public static IServiceCollection AddKookWebhookClient(
+ this IServiceCollection services,
Func, TClient> clientFactory, Action configure)
- where TClient: KookWebhookClient
- where TConfig: KookWebhookConfig
+ where TClient : KookWebhookClient
+ where TConfig : KookWebhookConfig
{
services.Configure(configure);
services.AddKookWebhookClient(clientFactory);
@@ -159,12 +162,15 @@ public static IServiceCollection AddKookWebhookClient(this ISe
/// Webhook 客户端的类型。
/// Webhook 客户端的配置类型。
/// 添加了 KOOK Webhook 客户端的服务集合。
- public static IServiceCollection AddKookWebhookClient(this IServiceCollection services,
+ public static IServiceCollection AddKookWebhookClient(
+ this IServiceCollection services,
Func, TClient> clientFactory)
- where TClient: KookWebhookClient
- where TConfig: KookWebhookConfig
+ where TClient : KookWebhookClient
+ where TConfig : KookWebhookConfig
{
- services.AddSingleton(provider => clientFactory(provider, provider.GetRequiredService>()));
+ services.AddSingleton(provider =>
+ clientFactory(provider, provider.GetRequiredService>()));
services.AddKookWebhookClient();
return services;
}
diff --git a/src/Kook.Net.Experimental/Net/Contexts/KookExperimentalJsonSerializerContexts.cs b/src/Kook.Net.Experimental/Net/Contexts/KookExperimentalJsonSerializerContexts.cs
new file mode 100644
index 00000000..d9b1118f
--- /dev/null
+++ b/src/Kook.Net.Experimental/Net/Contexts/KookExperimentalJsonSerializerContexts.cs
@@ -0,0 +1,15 @@
+using System.Text.Json.Serialization;
+using Kook.API.Rest;
+
+namespace Kook.Net.Contexts;
+
+///
+/// Provides JSON serialization context for Native AOT compatibility.
+///
+[JsonSourceGenerationOptions(
+ WriteIndented = false,
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+ NumberHandling = JsonNumberHandling.AllowReadingFromString)]
+[JsonSerializable(typeof(IReadOnlyCollection))]
+[JsonSerializable(typeof(ValidateCardsParams))]
+internal partial class KookExperimentalJsonSerializerContexts : JsonSerializerContext;
diff --git a/src/Kook.Net.Experimental/Rest/ExperimentalClientHelper.cs b/src/Kook.Net.Experimental/Rest/ExperimentalClientHelper.cs
index 241b8beb..690d09bb 100644
--- a/src/Kook.Net.Experimental/Rest/ExperimentalClientHelper.cs
+++ b/src/Kook.Net.Experimental/Rest/ExperimentalClientHelper.cs
@@ -39,7 +39,7 @@ public static Task ValidateCardsAsync(KookRestClient client, string cardsJson, R
#endregion
- #region MyRegion
+ #region ThreadTag
public static async Task> QueryThreadTagsAsync(KookRestClient client,
string keyword, RequestOptions? options)
diff --git a/src/Kook.Net.Experimental/Rest/KookRestApiClientExperimentalExtensions.cs b/src/Kook.Net.Experimental/Rest/KookRestApiClientExperimentalExtensions.cs
index 81995710..ad7bb32e 100644
--- a/src/Kook.Net.Experimental/Rest/KookRestApiClientExperimentalExtensions.cs
+++ b/src/Kook.Net.Experimental/Rest/KookRestApiClientExperimentalExtensions.cs
@@ -4,17 +4,27 @@
using Kook.API;
using Kook.API.Rest;
+using Kook.Net.Contexts;
using Kook.Net.Queue;
namespace Kook.Rest.Extensions;
internal static class KookRestApiClientExperimentalExtensions
{
+ private static readonly ConcurrentHashSet _injectedInstances = [];
+
+ private static void EnsureJsonTypeInfosInjection(this KookRestApiClient client)
+ {
+ if (_injectedInstances.TryAdd(client.GetHashCode()))
+ client.InjectJsonTypeInfos(KookExperimentalJsonSerializerContexts.Default.Options.TypeInfoResolverChain);
+ }
+
#region Guilds
public static IAsyncEnumerable> GetAdminGuildsAsync(this KookRestApiClient client,
int limit = KookConfig.MaxItemsPerBatchByDefault, int fromPage = 1, RequestOptions? options = null)
{
+ client.EnsureJsonTypeInfosInjection();
options = RequestOptions.CreateOrClone(options);
KookRestApiClient.BucketIds ids = new();
return client.SendPagedAsync(HttpMethod.Get,
@@ -29,6 +39,7 @@ public static IAsyncEnumerable> GetAdminGuildsAsync(t
public static async Task ValidateCardsAsync(this KookRestApiClient client,
ValidateCardsParams args, RequestOptions? options = null)
{
+ client.EnsureJsonTypeInfosInjection();
Preconditions.NotNull(args, nameof(args));
Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content));
options = RequestOptions.CreateOrClone(options);
@@ -46,6 +57,7 @@ await client.SendJsonAsync(HttpMethod.Post,
public static async Task> QueryThreadTagsAsync(this KookRestApiClient client,
string keyword, RequestOptions? options = null)
{
+ client.EnsureJsonTypeInfosInjection();
Preconditions.NotNullOrEmpty(keyword, nameof(keyword));
options = RequestOptions.CreateOrClone(options);
diff --git a/src/Kook.Net.Hosting/KookClientConfiguratorExtensions.cs b/src/Kook.Net.Hosting/KookClientConfiguratorExtensions.cs
index 854fafde..7685b4ba 100644
--- a/src/Kook.Net.Hosting/KookClientConfiguratorExtensions.cs
+++ b/src/Kook.Net.Hosting/KookClientConfiguratorExtensions.cs
@@ -1,4 +1,5 @@
-using Kook.Net.DependencyInjection.Microsoft;
+using System.Diagnostics.CodeAnalysis;
+using Kook.Net.DependencyInjection.Microsoft;
using Kook.Rest;
using Kook.Webhook;
using Kook.WebSocket;
@@ -140,7 +141,8 @@ public static IKookClientConfigurator UseHos
/// 客户端的类型。
/// 配置的类型。
/// 配置了基于 Webhook 的网关客户端及客户端托管服务的 KOOK 客户端配置器。
- public static IKookClientConfigurator UseHostedWebhookClient(
+ public static IKookClientConfigurator UseHostedWebhookClient(
this IKookClientServiceConfigurator configurator, Func, TClient> clientFactory,
Action configure, TokenType tokenType, string token, bool validateToken = true)
where TClient : KookWebhookClient
@@ -161,7 +163,8 @@ public static IKookClientConfigurator UseHostedWebhookClient 客户端的类型。
/// 配置的类型。
/// 配置了基于 Webhook 的网关客户端及客户端托管服务的 KOOK 客户端配置器。
- public static IKookClientConfigurator UseHostedWebhookClient(
+ public static IKookClientConfigurator UseHostedWebhookClient(
this IKookClientServiceConfigurator configurator, Func, TClient> clientFactory,
Action configure, Func tokenType,
Func token, Func? validateToken = null)
diff --git a/src/Kook.Net.Hosting/KookClientHostExtensions.cs b/src/Kook.Net.Hosting/KookClientHostExtensions.cs
index a23f966e..7475f599 100644
--- a/src/Kook.Net.Hosting/KookClientHostExtensions.cs
+++ b/src/Kook.Net.Hosting/KookClientHostExtensions.cs
@@ -1,4 +1,5 @@
-using Kook.Net.DependencyInjection.Microsoft;
+using System.Diagnostics.CodeAnalysis;
+using Kook.Net.DependencyInjection.Microsoft;
using Kook.Rest;
using Kook.Webhook;
using Kook.WebSocket;
@@ -195,7 +196,8 @@ public static IServiceCollection AddHostedKookSocketClient(this IServiceCollecti
/// 客户端的类型。
/// 配置的类型。
/// 添加了基于 Webhook 的 KOOK 网关客户端及服务的服务集合。
- public static IServiceCollection AddHostedKookWebhookClient(this IServiceCollection services,
+ public static IServiceCollection AddHostedKookWebhookClient(this IServiceCollection services,
Func, TClient> clientFactory, Action configure,
Func tokenType, Func token,
Func? validateToken = null)
@@ -255,7 +257,8 @@ public static IServiceCollection AddHostedKookWebhookClient(th
/// 客户端的类型。
/// 配置的类型。
/// 添加了基于 Webhook 的 KOOK 网关客户端及服务的服务集合。
- public static IServiceCollection AddHostedKookWebhookClient(this IServiceCollection services,
+ public static IServiceCollection AddHostedKookWebhookClient(this IServiceCollection services,
Func, TClient> clientFactory, Action configure,
TokenType tokenType, string token, bool validateToken = true)
where TClient : KookWebhookClient
diff --git a/src/Kook.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Kook.Net.Rest/Entities/Channels/ChannelHelper.cs
index dd5d191c..5a93757c 100644
--- a/src/Kook.Net.Rest/Entities/Channels/ChannelHelper.cs
+++ b/src/Kook.Net.Rest/Entities/Channels/ChannelHelper.cs
@@ -1,7 +1,10 @@
using Kook.API;
using Kook.API.Rest;
using System.Collections.Immutable;
+using System.Text.Encodings.Web;
using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Text.Json.Serialization.Metadata;
using Model = Kook.API.Channel;
namespace Kook.Rest;
@@ -226,12 +229,19 @@ public static async Task> SendMessageAsync(IMes
BaseKookClient client, MessageType messageType, ulong templateId, T parameters, IQuote? quote,
IUser? ephemeralUser, JsonSerializerOptions? jsonSerializerOptions, RequestOptions? options)
{
+ JsonSerializerOptions serializerOptions = jsonSerializerOptions ?? new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ NumberHandling = JsonNumberHandling.AllowReadingFromString
+ };
+ JsonTypeInfo typeInfo = serializerOptions.GetTypeInfo(typeof(T));
+
CreateMessageParams args = new()
{
Type = messageType,
ChannelId = channel.Id,
TemplateId = templateId,
- Content = JsonSerializer.Serialize(parameters, jsonSerializerOptions),
+ Content = JsonSerializer.Serialize(parameters, typeInfo),
QuotedMessageId = MessageHelper.QuoteToReferenceMessageId(quote),
ReplyMessageId = MessageHelper.QuoteToReplyMessageId(quote),
EphemeralUserId = ephemeralUser?.Id
@@ -252,7 +262,13 @@ public static async Task> SendCardsAsync(IMessa
BaseKookClient client, ulong templateId, T parameters, IQuote? quote, IUser? ephemeralUser,
JsonSerializerOptions? jsonSerializerOptions, RequestOptions? options)
{
- string json = JsonSerializer.Serialize(parameters, jsonSerializerOptions);
+ JsonSerializerOptions serializerOptions = jsonSerializerOptions ?? new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ NumberHandling = JsonNumberHandling.AllowReadingFromString
+ };
+ JsonTypeInfo typeInfo = serializerOptions.GetTypeInfo(typeof(T));
+ string json = JsonSerializer.Serialize(parameters, typeInfo);
return await SendMessageAsync(channel, client, MessageType.Card, templateId, json, quote, ephemeralUser, jsonSerializerOptions, options);
}
@@ -437,12 +453,19 @@ public static async Task> SendDirectMessageAsync(I
public static async Task> SendDirectMessageAsync(IDMChannel channel, BaseKookClient client,
MessageType messageType, ulong templateId, T parameters, IQuote? quote, JsonSerializerOptions? jsonSerializerOptions, RequestOptions? options)
{
+ JsonSerializerOptions serializerOptions = jsonSerializerOptions ?? new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ NumberHandling = JsonNumberHandling.AllowReadingFromString
+ };
+ JsonTypeInfo typeInfo = serializerOptions.GetTypeInfo(typeof(T));
+
CreateDirectMessageParams args = new()
{
Type = messageType,
UserId = channel.Recipient.Id,
TemplateId = templateId,
- Content = JsonSerializer.Serialize(parameters, jsonSerializerOptions),
+ Content = JsonSerializer.Serialize(parameters, typeInfo),
QuotedMessageId = MessageHelper.QuoteToReferenceMessageId(quote),
ReplyMessageId = MessageHelper.QuoteToReplyMessageId(quote)
};
@@ -462,7 +485,13 @@ public static Task> SendDirectCardsAsync(IDMChanne
public static Task> SendDirectCardsAsync(IDMChannel channel, BaseKookClient client,
ulong templateId, T parameters, IQuote? quote, JsonSerializerOptions? jsonSerializerOptions, RequestOptions? options)
{
- string json = JsonSerializer.Serialize(parameters, jsonSerializerOptions);
+ JsonSerializerOptions serializerOptions = jsonSerializerOptions ?? new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ NumberHandling = JsonNumberHandling.AllowReadingFromString
+ };
+ JsonTypeInfo typeInfo = serializerOptions.GetTypeInfo(typeof(T));
+ string json = JsonSerializer.Serialize(parameters, typeInfo);
return SendDirectMessageAsync(channel, client, MessageType.Card, templateId, json, quote, jsonSerializerOptions, options);
}
diff --git a/src/Kook.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Kook.Net.Rest/Entities/Messages/MessageHelper.cs
index 01c0feaf..d330e02d 100644
--- a/src/Kook.Net.Rest/Entities/Messages/MessageHelper.cs
+++ b/src/Kook.Net.Rest/Entities/Messages/MessageHelper.cs
@@ -5,7 +5,9 @@
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
+using System.Text.Json.Serialization.Metadata;
using System.Text.RegularExpressions;
+using Kook.Net.Contexts;
using UserModel = Kook.API.User;
namespace Kook.Rest;
@@ -249,11 +251,18 @@ public static async Task ModifyAsync(Guid msgId, BaseKookClient client, string c
public static async Task ModifyAsync(Guid msgId, BaseKookClient client, ulong templateId, T parameters,
IQuote? quote, IUser? ephemeralUser, JsonSerializerOptions? jsonSerializerOptions, RequestOptions? options)
{
+ JsonSerializerOptions serializerOptions = jsonSerializerOptions ?? new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ NumberHandling = JsonNumberHandling.AllowReadingFromString
+ };
+ JsonTypeInfo typeInfo = serializerOptions.GetTypeInfo(typeof(T));
+
ModifyMessageParams args = new()
{
MessageId = msgId,
TemplateId = templateId,
- Content = JsonSerializer.Serialize(parameters, jsonSerializerOptions),
+ Content = JsonSerializer.Serialize(parameters, typeInfo),
QuotedMessageId = QuoteToReferenceMessageId(quote),
ReplyMessageId = QuoteToReplyMessageId(quote),
EphemeralUserId = ephemeralUser?.Id
@@ -350,11 +359,18 @@ public static async Task ModifyDirectAsync(Guid msgId, BaseKookClient client,
public static async Task ModifyDirectAsync(Guid msgId, BaseKookClient client,
ulong templateId, T parameters, IQuote? quote, JsonSerializerOptions? jsonSerializerOptions, RequestOptions? options)
{
+ JsonSerializerOptions serializerOptions = jsonSerializerOptions ?? new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ NumberHandling = JsonNumberHandling.AllowReadingFromString
+ };
+ JsonTypeInfo typeInfo = serializerOptions.GetTypeInfo(typeof(T));
+
ModifyDirectMessageParams args = new()
{
TemplateId = templateId,
MessageId = msgId,
- Content = JsonSerializer.Serialize(parameters, jsonSerializerOptions),
+ Content = JsonSerializer.Serialize(parameters, typeInfo),
QuotedMessageId = QuoteToReferenceMessageId(quote),
ReplyMessageId = QuoteToReplyMessageId(quote)
};
@@ -395,15 +411,17 @@ public static async Task UnpinAsync(Guid messageId, ulong channelId, BaseKookCli
await client.ApiClient.UnpinAsync(args, options).ConfigureAwait(false);
}
+ private static readonly JsonSerializerOptions serializerOptions = new(KookRestJsonSerializerContext.Default.Options)
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false,
+ Converters = { CardConverterFactory.Instance }
+ };
+
public static ImmutableArray ParseCards(string json)
{
- JsonSerializerOptions serializerOptions = new()
- {
- Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
- NumberHandling = JsonNumberHandling.AllowReadingFromString,
- Converters = { CardConverterFactory.Instance }
- };
- CardBase[]? cardBases = JsonSerializer.Deserialize(json, serializerOptions);
+ JsonTypeInfo typeInfo = serializerOptions.GetTypedTypeInfo();
+ CardBase[]? cardBases = JsonSerializer.Deserialize(json, typeInfo);
if (cardBases is null)
throw new InvalidOperationException("Failed to parse cards from the provided JSON.");
return [..cardBases.Select(x => x.ToEntity())];
@@ -416,14 +434,9 @@ public static string SerializeCards(IEnumerable cards)
Preconditions.AtMost(enumerable.Sum(c => c.ModuleCount), maxModuleCount, nameof(cards),
$"A max of {maxModuleCount} modules can be included in a card.");
- JsonSerializerOptions serializerOptions = new()
- {
- Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
- NumberHandling = JsonNumberHandling.AllowReadingFromString,
- Converters = { CardConverterFactory.Instance },
- DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
- };
- return JsonSerializer.Serialize(enumerable.Select(c => c.ToModel()), serializerOptions);
+ CardBase[] cardBases = enumerable.Select(c => c.ToModel()).ToArray();
+ JsonTypeInfo typeInfo = serializerOptions.GetTypedTypeInfo();
+ return JsonSerializer.Serialize(cardBases, typeInfo);
}
public static IReadOnlyCollection ParseAttachments(IEnumerable cards)
diff --git a/src/Kook.Net.Rest/Entities/Threads/ThreadHelper.cs b/src/Kook.Net.Rest/Entities/Threads/ThreadHelper.cs
index 1988a43d..129c6ce4 100644
--- a/src/Kook.Net.Rest/Entities/Threads/ThreadHelper.cs
+++ b/src/Kook.Net.Rest/Entities/Threads/ThreadHelper.cs
@@ -1,6 +1,4 @@
using System.Collections.Immutable;
-using System.Runtime.CompilerServices;
-using System.Text.Json;
using Kook.API;
using Kook.API.Rest;
using Thread = Kook.API.Thread;
diff --git a/src/Kook.Net.Rest/Extensions/CardJsonExtension.cs b/src/Kook.Net.Rest/Extensions/CardJsonExtension.cs
index 4aeab0ab..5aea8019 100644
--- a/src/Kook.Net.Rest/Extensions/CardJsonExtension.cs
+++ b/src/Kook.Net.Rest/Extensions/CardJsonExtension.cs
@@ -1,8 +1,9 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Encodings.Web;
using System.Text.Json;
-using System.Text.Json.Serialization;
+using System.Text.Json.Serialization.Metadata;
using Kook.API;
+using Kook.Net.Contexts;
using Kook.Net.Converters;
namespace Kook.Rest;
@@ -12,12 +13,13 @@ namespace Kook.Rest;
///
public static class CardJsonExtension
{
- private static readonly Lazy _options = new(() => new JsonSerializerOptions
- {
- Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
- NumberHandling = JsonNumberHandling.AllowReadingFromString,
- Converters = { CardConverterFactory.Instance }
- });
+ private static readonly Lazy _options = new(() =>
+ new JsonSerializerOptions(KookRestJsonSerializerContext.Default.Options)
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ Converters = { CardConverterFactory.Instance }
+ }
+ );
///
/// 尝试将字符串解析为单个卡片构造器 。
@@ -29,7 +31,7 @@ public static bool TryParseSingle(string json, [NotNullWhen(true)] out ICardBuil
{
try
{
- CardBase? model = JsonSerializer.Deserialize(json, _options.Value);
+ CardBase? model = JsonSerializer.Deserialize(json, _options.Value.GetTypedTypeInfo());
if (model is not null)
{
@@ -57,7 +59,7 @@ public static bool TryParseMany(string json, [NotNullWhen(true)] out IEnumerable
{
try
{
- IEnumerable? models = JsonSerializer.Deserialize>(json, _options.Value);
+ IEnumerable? models = JsonSerializer.Deserialize(json, _options.Value.GetTypedTypeInfo>());
if (models is not null)
{
@@ -83,7 +85,7 @@ public static bool TryParseMany(string json, [NotNullWhen(true)] out IEnumerable
/// 如果无法将 JSON 解析为单个卡片构造器。
public static ICardBuilder ParseSingle(string json)
{
- CardBase model = JsonSerializer.Deserialize(json, _options.Value)
+ CardBase model = JsonSerializer.Deserialize(json, _options.Value.GetTypedTypeInfo())
?? throw new JsonException("Unable to parse json into card.");
return model.ToEntity().ToBuilder();
}
@@ -96,7 +98,8 @@ public static ICardBuilder ParseSingle(string json)
/// 如果无法将 JSON 解析为多个卡片构造器。
public static IEnumerable ParseMany(string json)
{
- IEnumerable models = JsonSerializer.Deserialize>(json, _options.Value)
+ JsonTypeInfo> typeInfo = _options.Value.GetTypedTypeInfo>();
+ IEnumerable models = JsonSerializer.Deserialize(json, typeInfo)
?? throw new JsonException("Unable to parse json into cards.");
return models.Select(x => x.ToEntity().ToBuilder());
}
@@ -118,14 +121,14 @@ public static string ToJsonString(this ICardBuilder builder, bool writeIndented
/// 包含来自 的数据的 JSON 字符串。
public static string ToJsonString(this ICard card, bool writeIndented = true)
{
- JsonSerializerOptions options = new()
+ JsonSerializerOptions options = new(KookRestJsonSerializerContext.Default.Options)
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
- NumberHandling = JsonNumberHandling.AllowReadingFromString,
WriteIndented = writeIndented,
- Converters = { CardConverterFactory.Instance },
- DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
+ Converters = { CardConverterFactory.Instance }
};
- return JsonSerializer.Serialize(card.ToModel(), options);
+ CardBase model = card.ToModel();
+ JsonTypeInfo typeInfo = options.GetTypeInfo(model.GetType());
+ return JsonSerializer.Serialize(model, typeInfo);
}
}
diff --git a/src/Kook.Net.Rest/Extensions/JsonSerializerOptionsExtensions.cs b/src/Kook.Net.Rest/Extensions/JsonSerializerOptionsExtensions.cs
new file mode 100644
index 00000000..cb1fc6db
--- /dev/null
+++ b/src/Kook.Net.Rest/Extensions/JsonSerializerOptionsExtensions.cs
@@ -0,0 +1,29 @@
+using System.Text.Json;
+using System.Text.Json.Serialization.Metadata;
+
+namespace Kook.Rest;
+
+///
+/// Provides extension methods for .
+///
+internal static class JsonSerializerOptionsExtensions
+{
+ ///
+ /// Gets the for the specified type from the .
+ ///
+ /// The type to get the type info for.
+ /// The serializer options containing the type resolver.
+ /// The for the specified type.
+ ///
+ /// Thrown when the type info cannot be resolved from the options.
+ ///
+ public static JsonTypeInfo GetTypedTypeInfo(this JsonSerializerOptions options)
+ {
+ if (options.TypeInfoResolver?.GetTypeInfo(typeof(T), options) is JsonTypeInfo typeInfo)
+ return typeInfo;
+
+ throw new InvalidOperationException(
+ $"Unable to resolve JsonTypeInfo for type {typeof(T).FullName}. " +
+ "Ensure the type is registered in the JsonSerializerContext.");
+ }
+}
diff --git a/src/Kook.Net.Rest/KookRestApiClient.cs b/src/Kook.Net.Rest/KookRestApiClient.cs
index 164cab6d..171282b7 100644
--- a/src/Kook.Net.Rest/KookRestApiClient.cs
+++ b/src/Kook.Net.Rest/KookRestApiClient.cs
@@ -12,11 +12,14 @@
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
+using System.Text.Json.Serialization.Metadata;
using Kook.API.Rest;
using Kook.Net;
+using Kook.Net.Contexts;
using Kook.Net.Converters;
using Kook.Net.Queue;
using Kook.Net.Rest;
+using Kook.Rest;
namespace Kook.API;
@@ -36,7 +39,7 @@ public event Func SentRequest
private readonly AsyncEvent> _sentRequestEvent = new();
- protected readonly JsonSerializerOptions _serializerOptions;
+ protected JsonSerializerOptions _serializerOptions;
protected readonly SemaphoreSlim _stateLock;
private readonly RestClientProvider _restClientProvider;
@@ -61,13 +64,8 @@ public KookRestApiClient(RestClientProvider restClientProvider, string userAgent
_restClientProvider = restClientProvider;
UserAgent = userAgent;
DefaultRetryMode = defaultRetryMode;
- _serializerOptions = serializerOptions
- ?? new JsonSerializerOptions
- {
- Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
- NumberHandling = JsonNumberHandling.AllowReadingFromString,
- Converters = { CardConverterFactory.Instance }
- };
+ _serializerOptions = EnsureJsonSerializerOptions(serializerOptions);
+ InjectJsonTypeInfos(KookRestJsonSerializerContext.Default.Options.TypeInfoResolverChain);
DefaultRatelimitCallback = defaultRatelimitCallback;
RequestQueue = new RequestQueue();
@@ -96,6 +94,27 @@ internal static string GetPrefixedToken(TokenType tokenType, string token) =>
_ => throw new ArgumentException("Unknown OAuth token type.", nameof(tokenType))
};
+ protected static JsonSerializerOptions EnsureJsonSerializerOptions(JsonSerializerOptions? jsonSerializerOptions)
+ {
+ JsonSerializerOptions options = jsonSerializerOptions is not null
+ ? new JsonSerializerOptions(jsonSerializerOptions)
+ : new JsonSerializerOptions();
+ options.WriteIndented = false;
+ options.NumberHandling = JsonNumberHandling.AllowReadingFromString;
+ options.Converters.Add(CardConverterFactory.Instance);
+ return options;
+ }
+
+ protected internal void InjectJsonTypeInfos(IList typeInfoResolvers)
+ {
+ JsonSerializerOptions serializerOptions = _serializerOptions.IsReadOnly
+ ? new JsonSerializerOptions(_serializerOptions)
+ : _serializerOptions;
+ foreach (IJsonTypeInfoResolver typeInfoResolver in typeInfoResolvers)
+ serializerOptions.TypeInfoResolverChain.Add(typeInfoResolver);
+ Interlocked.Exchange(ref _serializerOptions, serializerOptions);
+ }
+
internal virtual void Dispose(bool disposing)
{
if (!_isDisposed)
@@ -273,7 +292,7 @@ public async Task SendAsync(HttpMethod method, string endp
Stream response = await SendInternalAsync(method, endpoint, request).ConfigureAwait(false);
return bypassDeserialization && response is TResponse responseObj
? responseObj
- : await DeserializeJsonAsync(response).ConfigureAwait(false);
+ : await DeserializeJsonAsync(response, _serializerOptions).ConfigureAwait(false);
}
internal async Task SendJsonAsync(HttpMethod method,
@@ -298,7 +317,7 @@ public async Task SendJsonAsync(HttpMethod method, string
Stream response = await SendInternalAsync(method, endpoint, request).ConfigureAwait(false);
return bypassDeserialization && response is TResponse responseObj
? responseObj
- : await DeserializeJsonAsync(response).ConfigureAwait(false);
+ : await DeserializeJsonAsync(response, _serializerOptions).ConfigureAwait(false);
}
internal Task SendMultipartAsync(HttpMethod method,
@@ -323,7 +342,7 @@ public async Task SendMultipartAsync(HttpMethod method,
Stream response = await SendInternalAsync(method, endpoint, request).ConfigureAwait(false);
return bypassDeserialization && response is TResponse responseObj
? responseObj
- : await DeserializeJsonAsync(response).ConfigureAwait(false);
+ : await DeserializeJsonAsync(response, _serializerOptions).ConfigureAwait(false);
}
private async Task SendInternalAsync(HttpMethod method, string endpoint, RestRequest request)
@@ -413,6 +432,10 @@ public async Task GetGuildAsync(ulong guildId, RequestOptions? op
return response?.UserCount;
}
+#if NET6_0_OR_GREATER
+ [UnconditionalSuppressMessage("AOT", "IL3050",
+ Justification = "Expression tree usage in bucket ID generation has fallback logic and works in AOT scenarios.")]
+#endif
public IAsyncEnumerable> GetGuildMembersAsync(ulong guildId,
Action? func = null,
int limit = KookConfig.MaxUsersPerBatch, int fromPage = 1, RequestOptions? options = null)
@@ -1976,14 +1999,23 @@ protected static double ToMilliseconds(Stopwatch stopwatch) =>
Math.Round((double)stopwatch.ElapsedTicks / Stopwatch.Frequency * 1000.0, 2);
[return: NotNullIfNotNull(nameof(payload))]
- protected string? SerializeJson(object? payload, JsonSerializerOptions? options = null) =>
- payload is null ? null : JsonSerializer.Serialize(payload, options ?? _serializerOptions);
+ protected string? SerializeJson(object? payload, JsonSerializerOptions? options = null)
+ {
+ if (payload is null)
+ return null;
+
+ JsonSerializerOptions serializerOptions = options ?? _serializerOptions;
+ JsonTypeInfo typeInfo = serializerOptions.GetTypeInfo(payload.GetType());
+ return JsonSerializer.Serialize(payload, typeInfo);
+ }
- protected async Task DeserializeJsonAsync(Stream jsonStream)
+ protected static async Task DeserializeJsonAsync(Stream jsonStream, JsonSerializerOptions jsonSerializerOptions)
+ where T : class
{
try
{
- T? jsonObject = await JsonSerializer.DeserializeAsync(jsonStream, _serializerOptions).ConfigureAwait(false);
+ JsonTypeInfo typeInfo = jsonSerializerOptions.GetTypedTypeInfo();
+ T? jsonObject = await JsonSerializer.DeserializeAsync(jsonStream, typeInfo).ConfigureAwait(false);
if (jsonObject is null)
throw new JsonException($"Failed to deserialize JSON to type {typeof(T).FullName}");
return jsonObject;
@@ -2048,7 +2080,16 @@ private static BucketId GetBucketId(HttpMethod httpMethod, BucketIds ids, Expres
{
Preconditions.NotNull(callingMethod, nameof(callingMethod));
ids.HttpMethod = httpMethod;
- return _bucketIdGenerators.GetOrAdd(callingMethod, x => CreateBucketId(endpointExpr))(ids);
+ Func func = _bucketIdGenerators.GetOrAdd(
+ callingMethod,
+#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER
+ static (_, arg) => CreateBucketId(arg),
+ endpointExpr
+#else
+ _ => CreateBucketId(endpointExpr)
+#endif
+ );
+ return func(ids);
}
private static BucketId GetBucketId(HttpMethod httpMethod, BucketIds ids, Expression> endpointExpr,
@@ -2056,12 +2097,25 @@ private static BucketId GetBucketId(HttpMethod httpMethod, BucketI
{
Preconditions.NotNull(callingMethod, nameof(callingMethod));
ids.HttpMethod = httpMethod;
- return _bucketIdGenerators.GetOrAdd(callingMethod, x => CreateBucketId(endpointExpr, arg1, arg2))(ids);
+ Func func = _bucketIdGenerators.GetOrAdd(
+ callingMethod,
+#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER
+ static (_, arg) => CreateBucketId(arg.endpointExpr, arg.arg1, arg.arg2),
+ (endpointExpr, arg1, arg2)
+#else
+ _ => CreateBucketId(endpointExpr, arg1, arg2)
+#endif
+ );
+ return func(ids);
}
private static Func CreateBucketId(Expression> endpoint, TArg1 arg1, TArg2 arg2) =>
CreateBucketId(() => endpoint.Compile().Invoke(arg1, arg2));
+#if NET6_0_OR_GREATER
+ [UnconditionalSuppressMessage("AOT", "IL3050",
+ Justification = "Expression tree compilation is only used for bucket ID generation and has fallback logic.")]
+#endif
private static Func CreateBucketId(Expression> endpoint)
{
try
diff --git a/src/Kook.Net.Rest/Net/Contexts/KookRestJsonSerializerContext.cs b/src/Kook.Net.Rest/Net/Contexts/KookRestJsonSerializerContext.cs
new file mode 100644
index 00000000..00c762f0
--- /dev/null
+++ b/src/Kook.Net.Rest/Net/Contexts/KookRestJsonSerializerContext.cs
@@ -0,0 +1,198 @@
+using System.Text.Json.Serialization;
+// ReSharper disable RedundantNameQualifier
+
+namespace Kook.Net.Contexts;
+
+///
+/// Provides JSON serialization context for Native AOT compatibility.
+///
+[JsonSourceGenerationOptions(
+ WriteIndented = false,
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+ NumberHandling = JsonNumberHandling.AllowReadingFromString)]
+// REST Common Models
+[JsonSerializable(typeof(Kook.API.Rest.RestResponseBase))]
+// Common Models
+[JsonSerializable(typeof(Kook.API.Attachment))]
+[JsonSerializable(typeof(Kook.API.Ban))]
+[JsonSerializable(typeof(Kook.API.Channel))]
+[JsonSerializable(typeof(Kook.API.DirectMessage))]
+[JsonSerializable(typeof(Kook.API.Emoji))]
+[JsonSerializable(typeof(Kook.API.ExtendedThread))]
+[JsonSerializable(typeof(Kook.API.ExtendedThreadPost))]
+[JsonSerializable(typeof(Kook.API.Game))]
+[JsonSerializable(typeof(Kook.API.Guild))]
+[JsonSerializable(typeof(Kook.API.GuildCertification))]
+[JsonSerializable(typeof(Kook.API.GuildMentionInfo))]
+[JsonSerializable(typeof(Kook.API.Intimacy))]
+[JsonSerializable(typeof(Kook.API.Invite))]
+[JsonSerializable(typeof(Kook.API.MentionedChannel))]
+[JsonSerializable(typeof(Kook.API.MentionedUser))]
+[JsonSerializable(typeof(Kook.API.MentionInfo))]
+[JsonSerializable(typeof(Kook.API.Message))]
+[JsonSerializable(typeof(Kook.API.MessageTemplate))]
+[JsonSerializable(typeof(Kook.API.Nameplate))]
+[JsonSerializable(typeof(Kook.API.Quote))]
+[JsonSerializable(typeof(Kook.API.Reaction))]
+[JsonSerializable(typeof(Kook.API.RecommendInfo))]
+[JsonSerializable(typeof(Kook.API.Role))]
+[JsonSerializable(typeof(Kook.API.RolePermissionOverwrite))]
+[JsonSerializable(typeof(Kook.API.Thread))]
+[JsonSerializable(typeof(Kook.API.ThreadAttachmentType))]
+[JsonSerializable(typeof(Kook.API.ThreadMedia))]
+[JsonSerializable(typeof(Kook.API.ThreadPost))]
+[JsonSerializable(typeof(Kook.API.ThreadTag))]
+[JsonSerializable(typeof(Kook.API.User))]
+[JsonSerializable(typeof(Kook.API.UserChat))]
+[JsonSerializable(typeof(Kook.API.UserPermissionOverwrite))]
+[JsonSerializable(typeof(Kook.API.UserTag))]
+// Card Models
+[JsonSerializable(typeof(Kook.API.Card))]
+[JsonSerializable(typeof(Kook.API.CardBase))]
+[JsonSerializable(typeof(Kook.API.ICard))]
+[JsonSerializable(typeof(Kook.API.ActionGroupModule))]
+[JsonSerializable(typeof(Kook.API.AudioModule))]
+[JsonSerializable(typeof(Kook.API.ContainerModule))]
+[JsonSerializable(typeof(Kook.API.ContextModule))]
+[JsonSerializable(typeof(Kook.API.CountdownModule))]
+[JsonSerializable(typeof(Kook.API.DividerModule))]
+[JsonSerializable(typeof(Kook.API.FileModule))]
+[JsonSerializable(typeof(Kook.API.HeaderModule))]
+[JsonSerializable(typeof(Kook.API.ImageGroupModule))]
+[JsonSerializable(typeof(Kook.API.IModule))]
+[JsonSerializable(typeof(Kook.API.InviteModule))]
+[JsonSerializable(typeof(Kook.API.ModuleBase))]
+[JsonSerializable(typeof(Kook.API.SectionModule))]
+[JsonSerializable(typeof(Kook.API.VideoModule))]
+[JsonSerializable(typeof(Kook.API.ButtonElement))]
+[JsonSerializable(typeof(Kook.API.ElementBase))]
+[JsonSerializable(typeof(Kook.API.IElement))]
+[JsonSerializable(typeof(Kook.API.ImageElement))]
+[JsonSerializable(typeof(Kook.API.KMarkdownElement))]
+[JsonSerializable(typeof(Kook.API.ParagraphStruct))]
+[JsonSerializable(typeof(Kook.API.PlainTextElement))]
+// Embed Models
+[JsonSerializable(typeof(Kook.API.BilibiliVideoEmbed))]
+[JsonSerializable(typeof(Kook.API.CardEmbed))]
+[JsonSerializable(typeof(Kook.API.EmbedBase))]
+[JsonSerializable(typeof(Kook.API.IEmbed))]
+[JsonSerializable(typeof(Kook.API.ImageEmbed))]
+[JsonSerializable(typeof(Kook.API.LinkEmbed))]
+[JsonSerializable(typeof(Kook.API.NotImplementedEmbed))]
+// Poke Models
+[JsonSerializable(typeof(Kook.API.ImageAnimationPokeResource))]
+[JsonSerializable(typeof(Kook.API.IPokeResource))]
+[JsonSerializable(typeof(Kook.API.NotImplementedPokeResource))]
+[JsonSerializable(typeof(Kook.API.Poke))]
+[JsonSerializable(typeof(Kook.API.PokeQualityResource))]
+[JsonSerializable(typeof(Kook.API.PokeResourceBase))]
+// REST Models
+[JsonSerializable(typeof(Kook.API.Rest.AddOrRemoveRoleParams))]
+[JsonSerializable(typeof(Kook.API.Rest.AddOrRemoveRoleResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.AddReactionParams))]
+[JsonSerializable(typeof(Kook.API.Rest.BeginActivityParams))]
+[JsonSerializable(typeof(Kook.API.Rest.BlockUserParams))]
+[JsonSerializable(typeof(Kook.API.Rest.BoostSubscription))]
+[JsonSerializable(typeof(Kook.API.Rest.ColorMap))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateAssetParams))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateAssetResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateDirectMessageParams))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateDirectMessageResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateGameParams))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateGuildBanParams))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateGuildChannelParams))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateGuildEmoteParams))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateGuildInviteParams))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateGuildInviteResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateGuildRoleParams))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateMessageParams))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateMessageResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateMessageTemplateParams))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateOrModifyChannelPermissionOverwriteResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateOrModifyMessageTemplateResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateOrRemoveChannelPermissionOverwriteParams))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateOrRemoveGuildMuteDeafParams))]
+[JsonSerializable(typeof(Kook.API.Rest.CreatePipeMessageParams))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateThreadParams))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateThreadReplyParams))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateUserChatParams))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateVoiceGatewayParams))]
+[JsonSerializable(typeof(Kook.API.Rest.CreateVoiceGatewayResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.DeleteDirectMessageParams))]
+[JsonSerializable(typeof(Kook.API.Rest.DeleteGameParams))]
+[JsonSerializable(typeof(Kook.API.Rest.DeleteGuildChannelParams))]
+[JsonSerializable(typeof(Kook.API.Rest.DeleteGuildEmoteParams))]
+[JsonSerializable(typeof(Kook.API.Rest.DeleteGuildInviteParams))]
+[JsonSerializable(typeof(Kook.API.Rest.DeleteGuildRoleParams))]
+[JsonSerializable(typeof(Kook.API.Rest.DeleteMessageParams))]
+[JsonSerializable(typeof(Kook.API.Rest.DeleteMessageTemplateParams))]
+[JsonSerializable(typeof(Kook.API.Rest.DeleteThreadPostReplyParams))]
+[JsonSerializable(typeof(Kook.API.Rest.DeleteUserChatParams))]
+[JsonSerializable(typeof(Kook.API.Rest.DisposeVoiceGatewayParams))]
+[JsonSerializable(typeof(Kook.API.Rest.EndGameActivityParams))]
+[JsonSerializable(typeof(Kook.API.Rest.ExtendedGuild))]
+[JsonSerializable(typeof(Kook.API.Rest.FriendState), TypeInfoPropertyName = "ApiFriendState")]
+[JsonSerializable(typeof(Kook.API.Rest.GetBotGatewayResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.GetChannelPermissionOverwritesResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.GetFriendStatesResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.GetGuildMemberCountResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.GetGuildMuteDeafListResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.GetThreadCategoriesResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.GuildMember))]
+[JsonSerializable(typeof(Kook.API.Rest.HandleFriendRequestParams))]
+[JsonSerializable(typeof(Kook.API.Rest.KeepVoiceGatewayAliveParams))]
+[JsonSerializable(typeof(Kook.API.Rest.KickOutGuildMemberParams))]
+[JsonSerializable(typeof(Kook.API.Rest.KickOutVoiceChannelUserParams))]
+[JsonSerializable(typeof(Kook.API.Rest.LeaveGuildParams))]
+[JsonSerializable(typeof(Kook.API.Rest.ModifyChannelPermissionOverwriteParams))]
+[JsonSerializable(typeof(Kook.API.Rest.ModifyDirectMessageParams))]
+[JsonSerializable(typeof(Kook.API.Rest.ModifyGameParams))]
+[JsonSerializable(typeof(Kook.API.Rest.ModifyGuildChannelParams))]
+[JsonSerializable(typeof(Kook.API.Rest.ModifyGuildEmoteParams))]
+[JsonSerializable(typeof(Kook.API.Rest.ModifyGuildMemberNicknameParams))]
+[JsonSerializable(typeof(Kook.API.Rest.ModifyGuildRoleParams))]
+[JsonSerializable(typeof(Kook.API.Rest.ModifyMessageParams))]
+[JsonSerializable(typeof(Kook.API.Rest.ModifyMessageTemplateParams))]
+[JsonSerializable(typeof(Kook.API.Rest.ModifyTextChannelParams))]
+[JsonSerializable(typeof(Kook.API.Rest.ModifyThreadChannelParams))]
+[JsonSerializable(typeof(Kook.API.Rest.ModifyVoiceChannelParams))]
+[JsonSerializable(typeof(Kook.API.Rest.MoveUsersParams))]
+[JsonSerializable(typeof(Kook.API.Rest.MuteOrDeafType))]
+[JsonSerializable(typeof(Kook.API.Rest.PinUnpinMessageParams))]
+[JsonSerializable(typeof(Kook.API.Rest.QueryMessagesResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.QueryThreadsResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.QueryUserChatMessagesResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.ReactionUserResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.RemoveFriendParams))]
+[JsonSerializable(typeof(Kook.API.Rest.RemoveGuildBanParams))]
+[JsonSerializable(typeof(Kook.API.Rest.RemoveReactionParams))]
+[JsonSerializable(typeof(Kook.API.Rest.RequestFriendParams))]
+[JsonSerializable(typeof(Kook.API.Rest.RequestIntimacyParams))]
+[JsonSerializable(typeof(Kook.API.Rest.RestResponseBase))]
+[JsonSerializable(typeof(Kook.API.Rest.RichGuild))]
+[JsonSerializable(typeof(Kook.API.Rest.SelfOnlineStatusResponse))]
+[JsonSerializable(typeof(Kook.API.Rest.SelfUser))]
+[JsonSerializable(typeof(Kook.API.Rest.SortMode))]
+[JsonSerializable(typeof(Kook.API.Rest.SyncChannelPermissionsParams))]
+[JsonSerializable(typeof(Kook.API.Rest.ThreadCategory))]
+[JsonSerializable(typeof(Kook.API.Rest.ThreadCategoryPermissionOverwrite))]
+[JsonSerializable(typeof(Kook.API.Rest.UnblockUserParams))]
+[JsonSerializable(typeof(Kook.API.Rest.UnravelRelationParams))]
+[JsonSerializable(typeof(Kook.API.Rest.UpdateIntimacyValueParams))]
+[JsonSerializable(typeof(Kook.API.Rest.UserConfig))]
+// Pages Models
+[JsonSerializable(typeof(Kook.API.Rest.PagedResponseBase))]
+[JsonSerializable(typeof(Kook.API.Rest.PagedResponseBase))]
+[JsonSerializable(typeof(Kook.API.Rest.PagedResponseBase))]
+[JsonSerializable(typeof(Kook.API.Rest.PagedResponseBase))]
+[JsonSerializable(typeof(Kook.API.Rest.PagedResponseBase))]
+[JsonSerializable(typeof(Kook.API.Rest.PagedResponseBase))]
+[JsonSerializable(typeof(Kook.API.Rest.PagedResponseBase))]
+[JsonSerializable(typeof(Kook.API.Rest.PagedResponseBase))]
+[JsonSerializable(typeof(Kook.API.Rest.PagedResponseBase))]
+[JsonSerializable(typeof(Kook.API.Rest.PagedResponseBase))]
+[JsonSerializable(typeof(Kook.API.Rest.PagedResponseBase))]
+// Misc Models
+[JsonSerializable(typeof(IList))]
+[JsonSerializable(typeof(Kook.API.CardBase[]))]
+internal partial class KookRestJsonSerializerContext : JsonSerializerContext;
diff --git a/src/Kook.Net.Rest/Net/Converters/Cards/CardConverter.cs b/src/Kook.Net.Rest/Net/Converters/Cards/CardConverter.cs
index 20e01e7e..5931bfc9 100644
--- a/src/Kook.Net.Rest/Net/Converters/Cards/CardConverter.cs
+++ b/src/Kook.Net.Rest/Net/Converters/Cards/CardConverter.cs
@@ -2,6 +2,7 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
+using Kook.Rest;
namespace Kook.Net.Converters;
@@ -15,7 +16,7 @@ internal class CardConverter : JsonConverter
switch (jsonNode?["type"]?.GetValue())
{
case "card":
- return JsonSerializer.Deserialize(jsonNode.ToJsonString(), options);
+ return JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo());
default:
throw new ArgumentOutOfRangeException(nameof(CardType));
}
@@ -23,10 +24,10 @@ internal class CardConverter : JsonConverter
public override void Write(Utf8JsonWriter writer, CardBase value, JsonSerializerOptions options)
{
- switch (value.Type)
+ switch (value)
{
- case CardType.Card:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.Card, options));
+ case API.Card { Type: CardType.Card } card:
+ writer.WriteRawValue(JsonSerializer.Serialize(card, options.GetTypedTypeInfo()));
break;
default:
throw new ArgumentOutOfRangeException(nameof(CardType));
diff --git a/src/Kook.Net.Rest/Net/Converters/Cards/ElementConverter.cs b/src/Kook.Net.Rest/Net/Converters/Cards/ElementConverter.cs
index 4e9851c2..1dd77ca3 100644
--- a/src/Kook.Net.Rest/Net/Converters/Cards/ElementConverter.cs
+++ b/src/Kook.Net.Rest/Net/Converters/Cards/ElementConverter.cs
@@ -2,6 +2,7 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
+using Kook.Rest;
namespace Kook.Net.Converters;
@@ -14,33 +15,33 @@ internal class ElementConverter : JsonConverter
JsonNode? jsonNode = JsonNode.Parse(ref reader);
return jsonNode?["type"]?.GetValue() switch
{
- "plain-text" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
- "kmarkdown" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
- "image" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
- "button" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
- "paragraph" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
+ "plain-text" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
+ "kmarkdown" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
+ "image" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
+ "button" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
+ "paragraph" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
_ => throw new ArgumentOutOfRangeException(nameof(ElementType))
};
}
public override void Write(Utf8JsonWriter writer, ElementBase value, JsonSerializerOptions options)
{
- switch (value.Type)
+ switch (value)
{
- case ElementType.PlainText:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.PlainTextElement, options));
+ case API.PlainTextElement { Type: ElementType.PlainText } plaintext:
+ writer.WriteRawValue(JsonSerializer.Serialize(plaintext, options.GetTypedTypeInfo()));
break;
- case ElementType.KMarkdown:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.KMarkdownElement, options));
+ case API.KMarkdownElement { Type: ElementType.KMarkdown } kMarkdown:
+ writer.WriteRawValue(JsonSerializer.Serialize(kMarkdown, options.GetTypedTypeInfo()));
break;
- case ElementType.Image:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.ImageElement, options));
+ case API.ImageElement { Type: ElementType.Image } image:
+ writer.WriteRawValue(JsonSerializer.Serialize(image, options.GetTypedTypeInfo()));
break;
- case ElementType.Button:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.ButtonElement, options));
+ case API.ButtonElement { Type: ElementType.Button } button:
+ writer.WriteRawValue(JsonSerializer.Serialize(button, options.GetTypedTypeInfo()));
break;
- case ElementType.Paragraph:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.ParagraphStruct, options));
+ case API.ParagraphStruct { Type: ElementType.Paragraph } paragraph:
+ writer.WriteRawValue(JsonSerializer.Serialize(paragraph, options.GetTypedTypeInfo()));
break;
default:
throw new ArgumentOutOfRangeException(nameof(ElementType));
diff --git a/src/Kook.Net.Rest/Net/Converters/Cards/ModuleConverter.cs b/src/Kook.Net.Rest/Net/Converters/Cards/ModuleConverter.cs
index 8355edaa..6ffd7c81 100644
--- a/src/Kook.Net.Rest/Net/Converters/Cards/ModuleConverter.cs
+++ b/src/Kook.Net.Rest/Net/Converters/Cards/ModuleConverter.cs
@@ -2,6 +2,7 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
+using Kook.Rest;
namespace Kook.Net.Converters;
@@ -14,61 +15,61 @@ internal class ModuleConverter : JsonConverter
JsonNode? jsonNode = JsonNode.Parse(ref reader);
return jsonNode?["type"]?.GetValue() switch
{
- "header" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
- "section" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
- "image-group" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
- "container" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
- "action-group" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
- "context" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
- "divider" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
- "file" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
- "audio" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
- "video" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
- "countdown" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
- "invite" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
+ "header" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
+ "section" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
+ "image-group" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
+ "container" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
+ "action-group" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
+ "context" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
+ "divider" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
+ "file" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
+ "audio" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
+ "video" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
+ "countdown" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
+ "invite" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
_ => throw new ArgumentOutOfRangeException(nameof(CardType))
};
}
public override void Write(Utf8JsonWriter writer, ModuleBase value, JsonSerializerOptions options)
{
- switch (value.Type)
+ switch (value)
{
- case ModuleType.Header:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.HeaderModule, options));
+ case API.HeaderModule { Type: ModuleType.Header } header:
+ writer.WriteRawValue(JsonSerializer.Serialize(header, options.GetTypedTypeInfo()));
break;
- case ModuleType.Section:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.SectionModule, options));
+ case API.SectionModule { Type: ModuleType.Section } section:
+ writer.WriteRawValue(JsonSerializer.Serialize(section, options.GetTypedTypeInfo()));
break;
- case ModuleType.ImageGroup:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.ImageGroupModule, options));
+ case API.ImageGroupModule { Type: ModuleType.ImageGroup } imageGroup:
+ writer.WriteRawValue(JsonSerializer.Serialize(imageGroup, options.GetTypedTypeInfo()));
break;
- case ModuleType.Container:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.ContainerModule, options));
+ case API.ContainerModule { Type: ModuleType.Container } container:
+ writer.WriteRawValue(JsonSerializer.Serialize(container, options.GetTypedTypeInfo()));
break;
- case ModuleType.ActionGroup:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.ActionGroupModule, options));
+ case API.ActionGroupModule { Type: ModuleType.ActionGroup } actionGroup:
+ writer.WriteRawValue(JsonSerializer.Serialize(actionGroup, options.GetTypedTypeInfo()));
break;
- case ModuleType.Context:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.ContextModule, options));
+ case API.ContextModule { Type: ModuleType.Context } context:
+ writer.WriteRawValue(JsonSerializer.Serialize(context, options.GetTypedTypeInfo()));
break;
- case ModuleType.Divider:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.DividerModule, options));
+ case API.DividerModule { Type: ModuleType.Divider } divider:
+ writer.WriteRawValue(JsonSerializer.Serialize(divider, options.GetTypedTypeInfo()));
break;
- case ModuleType.File:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.FileModule, options));
+ case API.FileModule { Type: ModuleType.File } file:
+ writer.WriteRawValue(JsonSerializer.Serialize(file, options.GetTypedTypeInfo()));
break;
- case ModuleType.Audio:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.AudioModule, options));
+ case API.AudioModule { Type: ModuleType.Audio } audio:
+ writer.WriteRawValue(JsonSerializer.Serialize(audio, options.GetTypedTypeInfo()));
break;
- case ModuleType.Video:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.VideoModule, options));
+ case API.VideoModule { Type: ModuleType.Video } video:
+ writer.WriteRawValue(JsonSerializer.Serialize(video, options.GetTypedTypeInfo()));
break;
- case ModuleType.Countdown:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.CountdownModule, options));
+ case API.CountdownModule { Type: ModuleType.Countdown } countdown:
+ writer.WriteRawValue(JsonSerializer.Serialize(countdown, options.GetTypedTypeInfo()));
break;
- case ModuleType.Invite:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.InviteModule, options));
+ case API.InviteModule { Type: ModuleType.Invite } invite:
+ writer.WriteRawValue(JsonSerializer.Serialize(invite, options.GetTypedTypeInfo()));
break;
default:
throw new ArgumentOutOfRangeException(nameof(ModuleType));
diff --git a/src/Kook.Net.Rest/Net/Converters/Embeds/EmbedConverter.cs b/src/Kook.Net.Rest/Net/Converters/Embeds/EmbedConverter.cs
index 9e119e11..8ec07438 100644
--- a/src/Kook.Net.Rest/Net/Converters/Embeds/EmbedConverter.cs
+++ b/src/Kook.Net.Rest/Net/Converters/Embeds/EmbedConverter.cs
@@ -2,6 +2,7 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
+using Kook.Rest;
namespace Kook.Net.Converters;
@@ -15,29 +16,29 @@ internal class EmbedConverter : JsonConverter
if (rawType == null) return null;
return rawType switch
{
- "link" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
- "image" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
- "bili-video" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
- "card" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
+ "link" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
+ "image" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
+ "bili-video" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
+ "card" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
_ => new API.NotImplementedEmbed(rawType, jsonNode)
};
}
public override void Write(Utf8JsonWriter writer, EmbedBase value, JsonSerializerOptions options)
{
- switch (value.Type)
+ switch (value)
{
- case EmbedType.Link:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.LinkEmbed, options));
+ case API.LinkEmbed { Type: EmbedType.Link } link:
+ writer.WriteRawValue(JsonSerializer.Serialize(link, options.GetTypedTypeInfo()));
break;
- case EmbedType.Image:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.ImageEmbed, options));
+ case API.ImageEmbed { Type: EmbedType.Image } image:
+ writer.WriteRawValue(JsonSerializer.Serialize(image, options.GetTypedTypeInfo()));
break;
- case EmbedType.BilibiliVideo:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.BilibiliVideoEmbed, options));
+ case API.BilibiliVideoEmbed { Type: EmbedType.BilibiliVideo } bilibiliVideo:
+ writer.WriteRawValue(JsonSerializer.Serialize(bilibiliVideo, options.GetTypedTypeInfo()));
break;
- case EmbedType.Card:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.CardEmbed, options));
+ case API.CardEmbed { Type: EmbedType.Card } card:
+ writer.WriteRawValue(JsonSerializer.Serialize(card, options.GetTypedTypeInfo()));
break;
default:
writer.WriteRawValue((value as API.NotImplementedEmbed)!.RawJsonNode.ToString());
diff --git a/src/Kook.Net.Rest/Net/Converters/GuildFeaturesConverter.cs b/src/Kook.Net.Rest/Net/Converters/GuildFeaturesConverter.cs
index dac62e06..0086bbd7 100644
--- a/src/Kook.Net.Rest/Net/Converters/GuildFeaturesConverter.cs
+++ b/src/Kook.Net.Rest/Net/Converters/GuildFeaturesConverter.cs
@@ -1,5 +1,6 @@
using System.Text.Json;
using System.Text.Json.Serialization;
+using Kook.Rest;
namespace Kook.Net.Converters;
@@ -7,7 +8,7 @@ internal class GuildFeaturesConverter : JsonConverter
{
public override GuildFeatures Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
- IList rawValues = JsonSerializer.Deserialize>(ref reader, options) ?? [];
+ IList rawValues = JsonSerializer.Deserialize(ref reader, options.GetTypedTypeInfo>()) ?? [];
GuildFeature features = rawValues.Aggregate(GuildFeature.None,
(current, item) => current | ApiStringToFeature(item));
return new GuildFeatures(features, rawValues);
@@ -15,12 +16,15 @@ public override GuildFeatures Read(ref Utf8JsonReader reader, Type typeToConvert
public override void Write(Utf8JsonWriter writer, GuildFeatures value, JsonSerializerOptions options)
{
- Array enumValues = Enum.GetValues(typeof(GuildFeature));
+#if NET8_0_OR_GREATER
+ IEnumerable values = Enum.GetValues();
+#else
+ IEnumerable values = Enum.GetValues(typeof(GuildFeature)).Cast();
+#endif
writer.WriteStartArray();
- foreach (object enumValue in enumValues)
+ foreach (GuildFeature val in values)
{
- GuildFeature val = (GuildFeature)enumValue;
if (val is GuildFeature.None)
continue;
diff --git a/src/Kook.Net.Rest/Net/Converters/Pokes/PokeResourceConverter.cs b/src/Kook.Net.Rest/Net/Converters/Pokes/PokeResourceConverter.cs
index e8d38c4c..d89be4f5 100644
--- a/src/Kook.Net.Rest/Net/Converters/Pokes/PokeResourceConverter.cs
+++ b/src/Kook.Net.Rest/Net/Converters/Pokes/PokeResourceConverter.cs
@@ -2,6 +2,7 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
+using Kook.Rest;
namespace Kook.Net.Converters;
@@ -15,17 +16,17 @@ internal class PokeResourceConverter : JsonConverter
if (rawType == null) return null;
return rawType switch
{
- "ImageAnimation" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options),
+ "ImageAnimation" => JsonSerializer.Deserialize(jsonNode.ToJsonString(), options.GetTypedTypeInfo()),
_ => new API.NotImplementedPokeResource(rawType, jsonNode) { Type = PokeResourceType.NotImplemented }
};
}
public override void Write(Utf8JsonWriter writer, PokeResourceBase value, JsonSerializerOptions options)
{
- switch (value.Type)
+ switch (value)
{
- case PokeResourceType.ImageAnimation:
- writer.WriteRawValue(JsonSerializer.Serialize(value as API.ImageAnimationPokeResource, options));
+ case API.ImageAnimationPokeResource { Type : PokeResourceType.ImageAnimation } imageAnimation:
+ writer.WriteRawValue(JsonSerializer.Serialize(imageAnimation, options.GetTypedTypeInfo()));
break;
default:
writer.WriteRawValue((value as API.NotImplementedPokeResource)!.RawJsonNode.ToString());
diff --git a/src/Kook.Net.Rest/Net/Converters/QuoteConverter.cs b/src/Kook.Net.Rest/Net/Converters/QuoteConverter.cs
index baeb3dc4..674024b4 100644
--- a/src/Kook.Net.Rest/Net/Converters/QuoteConverter.cs
+++ b/src/Kook.Net.Rest/Net/Converters/QuoteConverter.cs
@@ -1,5 +1,6 @@
using System.Text.Json;
using System.Text.Json.Serialization;
+using Kook.Rest;
namespace Kook.Net.Converters;
@@ -14,7 +15,7 @@ internal class QuoteConverter : JsonConverter
{
JsonTokenType.Null or JsonTokenType.String => null,
// 此转换器不会直接标记在 API.Quote 上,而是标记在属性上,因此,直接对 reader 反序列化为 API.Quote 的操作不会使用此转换器。
- JsonTokenType.StartObject => JsonSerializer.Deserialize(ref reader, options),
+ JsonTokenType.StartObject => JsonSerializer.Deserialize(ref reader, options.GetTypedTypeInfo()),
_ => throw new JsonException(
$"{nameof(QuoteConverter)} expects boolean, string or number token, but got {reader.TokenType}")
};
diff --git a/src/Kook.Net.Rest/Net/Converters/TypeIdPermissionOverwriteTargetTypeConverter.cs b/src/Kook.Net.Rest/Net/Converters/TypeIdPermissionOverwriteTargetTypeConverter.cs
index 4ada33f1..9ecf88e0 100644
--- a/src/Kook.Net.Rest/Net/Converters/TypeIdPermissionOverwriteTargetTypeConverter.cs
+++ b/src/Kook.Net.Rest/Net/Converters/TypeIdPermissionOverwriteTargetTypeConverter.cs
@@ -1,4 +1,3 @@
-using Kook.API.Rest;
using System.Text.Json;
using System.Text.Json.Serialization;
diff --git a/src/Kook.Net.Rest/Net/Converters/TypePermissionOverwriteTargetTypeConverter.cs b/src/Kook.Net.Rest/Net/Converters/TypePermissionOverwriteTargetTypeConverter.cs
index 7dc25ab9..c8e24885 100644
--- a/src/Kook.Net.Rest/Net/Converters/TypePermissionOverwriteTargetTypeConverter.cs
+++ b/src/Kook.Net.Rest/Net/Converters/TypePermissionOverwriteTargetTypeConverter.cs
@@ -1,6 +1,5 @@
using System.Text.Json;
using System.Text.Json.Serialization;
-using Kook.API.Rest;
namespace Kook.Net.Converters;
diff --git a/src/Kook.Net.Rest/Net/DefaultRestClient.cs b/src/Kook.Net.Rest/Net/DefaultRestClient.cs
index 661c2842..48f8f2e1 100644
--- a/src/Kook.Net.Rest/Net/DefaultRestClient.cs
+++ b/src/Kook.Net.Rest/Net/DefaultRestClient.cs
@@ -13,7 +13,8 @@
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
-using System.Text.Json.Serialization;
+using Kook.Net.Contexts;
+using Kook.Net.Converters;
namespace Kook.Net.Rest;
@@ -44,10 +45,9 @@ public DefaultRestClient(string baseUrl, bool useProxy = false, IWebProxy? webPr
_cancellationToken = CancellationToken.None;
- _serializerOptions = new JsonSerializerOptions
+ _serializerOptions = new JsonSerializerOptions(KookRestJsonSerializerContext.Default.Options)
{
- Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
- NumberHandling = JsonNumberHandling.AllowReadingFromString
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
}
diff --git a/src/Kook.Net.Rest/Net/Queue/RequestBucket.cs b/src/Kook.Net.Rest/Net/Queue/RequestBucket.cs
index ccea65af..9190830b 100644
--- a/src/Kook.Net.Rest/Net/Queue/RequestBucket.cs
+++ b/src/Kook.Net.Rest/Net/Queue/RequestBucket.cs
@@ -2,8 +2,10 @@
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
-using System.Text.Json.Serialization;
+using Kook.Net.Contexts;
+using Kook.Net.Converters;
using Kook.Net.Rest;
+using Kook.Rest;
namespace Kook.Net.Queue;
@@ -24,10 +26,9 @@ internal class RequestBucket
public RequestBucket(RequestQueue queue, IRequest request, BucketId id)
{
- _serializerOptions = new JsonSerializerOptions
+ _serializerOptions = new JsonSerializerOptions(KookRestJsonSerializerContext.Default.Options)
{
- Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
- NumberHandling = JsonNumberHandling.AllowReadingFromString
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
_queue = queue;
Id = id;
@@ -97,8 +98,8 @@ public async Task SendAsync(RestRequest request)
if (response.Stream != null)
try
{
- responseBase = await JsonSerializer.DeserializeAsync(response.Stream,
- _serializerOptions);
+ responseBase = await JsonSerializer.DeserializeAsync(
+ response.Stream, _serializerOptions.GetTypedTypeInfo());
}
catch
{
@@ -120,8 +121,8 @@ public async Task SendAsync(RestRequest request)
KookDebugger.DebugRatelimit($"[Ratelimit] [{id}] Success");
if (response.MediaTypeHeader?.MediaType == "application/json")
{
- API.Rest.RestResponseBase? responseBase =
- await JsonSerializer.DeserializeAsync(response.Stream, _serializerOptions);
+ API.Rest.RestResponseBase? responseBase = await JsonSerializer.DeserializeAsync(
+ response.Stream, _serializerOptions.GetTypedTypeInfo());
if (responseBase?.Code > (KookErrorCode)0)
{
throw new HttpException(
diff --git a/src/Kook.Net.Rest/Net/Queue/RequestQueue.cs b/src/Kook.Net.Rest/Net/Queue/RequestQueue.cs
index af2c77aa..1ce1722f 100644
--- a/src/Kook.Net.Rest/Net/Queue/RequestQueue.cs
+++ b/src/Kook.Net.Rest/Net/Queue/RequestQueue.cs
@@ -164,7 +164,11 @@ internal async Task RaiseRateLimitTriggered(BucketId bucketId, RateLimitInfo? in
public void ClearGatewayBuckets()
{
- foreach (GatewayBucketType gwBucket in (GatewayBucketType[])Enum.GetValues(typeof(GatewayBucketType)))
+#if NET6_0_OR_GREATER
+ foreach (GatewayBucketType gwBucket in Enum.GetValues())
+#else
+ foreach (GatewayBucketType gwBucket in Enum.GetValues(typeof(GatewayBucketType)).Cast())
+#endif
_buckets.TryRemove(GatewayBucket.Get(gwBucket).Id, out _);
}
diff --git a/src/Kook.Net.WebSocket/KookSocketApiClient.cs b/src/Kook.Net.WebSocket/KookSocketApiClient.cs
index 547d9e01..d3583edc 100644
--- a/src/Kook.Net.WebSocket/KookSocketApiClient.cs
+++ b/src/Kook.Net.WebSocket/KookSocketApiClient.cs
@@ -1,13 +1,15 @@
using System.IO.Compression;
using System.Text;
-using System.Diagnostics;
using System.Text.Encodings.Web;
using System.Text.Json;
using Kook.API.Gateway;
using Kook.API.Rest;
+using Kook.Net.Contexts;
+using Kook.Net.Converters;
using Kook.Net.Queue;
using Kook.Net.Rest;
using Kook.Net.WebSockets;
+using Kook.Rest;
using Kook.WebSocket;
namespace Kook.API;
@@ -44,12 +46,6 @@ public event Func Disconnected
private Guid? _sessionId;
private int _lastSeq;
- private readonly JsonSerializerOptions _debugJsonSerializerOptions = new()
- {
- WriteIndented = true,
- Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
- };
-
public ConnectionState ConnectionState { get; private set; }
internal IWebSocketClient WebSocketClient { get; }
@@ -59,9 +55,10 @@ public KookSocketApiClient(RestClientProvider restClientProvider,
RetryMode defaultRetryMode = RetryMode.AlwaysRetry,
JsonSerializerOptions? serializerOptions = null,
Func? defaultRatelimitCallback = null)
- : base(restClientProvider, userAgent, acceptLanguage,
- defaultRetryMode, serializerOptions, defaultRatelimitCallback)
+ : base(restClientProvider, userAgent, acceptLanguage, defaultRetryMode,
+ serializerOptions, defaultRatelimitCallback)
{
+ InjectJsonTypeInfos(KookWebSocketJsonSerializerContext.Default.Options.TypeInfoResolverChain);
_gatewayUrl = url;
if (url != null)
_isExplicitUrl = true;
@@ -97,19 +94,15 @@ private async Task OnBinaryMessage(byte[] data, int index, int count)
decompressed.Position = 0;
GatewaySocketFrame? gatewaySocketFrame = await JsonSerializer
- .DeserializeAsync(decompressed, _serializerOptions);
+ .DeserializeAsync(decompressed, _serializerOptions.GetTypedTypeInfo());
if (gatewaySocketFrame is not null)
{
if (KookDebugger.IsDebuggingPacket)
{
string raw = Encoding.Default.GetString(decompressed.ToArray()).TrimEnd('\n');
- string parsed = JsonSerializer
- .Serialize(gatewaySocketFrame.Payload, _debugJsonSerializerOptions)
- .TrimEnd('\n');
KookDebugger.DebugPacket($"""
[{DateTimeOffset.Now:HH:mm:ss}] <- [{gatewaySocketFrame.Type}] : #{gatewaySocketFrame.Sequence}
[Raw] {raw}
- [Parsed] {parsed}
""");
}
JsonElement payloadElement = gatewaySocketFrame.Payload ?? EmptyJsonElement;
@@ -122,18 +115,14 @@ await _receivedGatewayEvent
private async Task OnTextMessage(string message)
{
- GatewaySocketFrame? gatewaySocketFrame = JsonSerializer.Deserialize(message, _serializerOptions);
+ GatewaySocketFrame? gatewaySocketFrame = JsonSerializer.Deserialize(message, _serializerOptions.GetTypedTypeInfo());
if (gatewaySocketFrame is null)
return;
if (KookDebugger.IsDebuggingPacket)
{
- string parsed = JsonSerializer
- .Serialize(gatewaySocketFrame.Payload, _debugJsonSerializerOptions)
- .TrimEnd('\n');
KookDebugger.DebugPacket($"""
[{DateTimeOffset.Now:HH:mm:ss}] <- [{gatewaySocketFrame.Type}] : #{gatewaySocketFrame.Sequence}
[Raw] {message}
- [Parsed] {parsed}
""");
}
JsonElement payloadElement = gatewaySocketFrame.Payload ?? EmptyJsonElement;
@@ -287,7 +276,7 @@ await RequestQueue
await _sentGatewayMessageEvent.InvokeAsync(gatewaySocketFrameType).ConfigureAwait(false);
if (KookDebugger.IsDebuggingPacket)
{
- string payloadString = JsonSerializer.Serialize(payload, _debugJsonSerializerOptions);
+ string payloadString = JsonSerializer.Serialize(payload, _serializerOptions.GetTypedTypeInfo());
KookDebugger.DebugPacket(
$"[{DateTimeOffset.Now:HH:mm:ss}] -> [{gatewaySocketFrameType}] : #{sequence} \n{payloadString}".TrimEnd('\n'));
}
diff --git a/src/Kook.Net.WebSocket/KookSocketClient.Messages.cs b/src/Kook.Net.WebSocket/KookSocketClient.Messages.cs
index 5571d7bf..b8be7d8b 100644
--- a/src/Kook.Net.WebSocket/KookSocketClient.Messages.cs
+++ b/src/Kook.Net.WebSocket/KookSocketClient.Messages.cs
@@ -4,7 +4,6 @@
using Kook.API;
using Kook.API.Gateway;
using Kook.API.Rest;
-using Kook.Audio;
using Kook.Net;
using Kook.Rest;
@@ -2100,14 +2099,14 @@ private Cacheable GetCacheableMessage(SocketMessage? value,
private T? DeserializePayload(JsonElement jsonElement)
{
- if (jsonElement.Deserialize(_serializerOptions) is { } x) return x;
+ if (jsonElement.Deserialize(_serializerOptions.GetTypedTypeInfo