serialization

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Serialization in .NET

.NET中的序列化

When to Use This Skill

何时使用该技能

Use this skill when:
  • Choosing a serialization format for APIs, messaging, or persistence
  • Migrating from Newtonsoft.Json to System.Text.Json
  • Implementing AOT-compatible serialization
  • Designing wire formats for distributed systems
  • Optimizing serialization performance

当你遇到以下场景时可使用该技能:
  • 为API、消息传输或持久化选择序列化格式
  • 从Newtonsoft.Json迁移到System.Text.Json
  • 实现兼容AOT的序列化能力
  • 为分布式系统设计传输格式
  • 优化序列化性能

Serialization Format Comparison

序列化格式对比

FormatLibraryAOT-SafeHuman-ReadableRelative SizeRelative SpeedBest For
JSONSystem.Text.Json (source gen)YesYesLargestGoodAPIs, config, web clients
ProtobufGoogle.ProtobufYesNoSmallestFastestService-to-service, gRPC wire format
MessagePackMessagePack-CSharpYes (with AOT resolver)NoSmallFastHigh-throughput caching, real-time
JSONNewtonsoft.JsonNo (reflection)YesLargestSlowerLegacy only -- do not use for AOT
格式依赖库AOT兼容人类可读相对体积相对速度适用场景
JSONSystem.Text.Json (源生成器)最大良好APIs、配置、Web客户端
ProtobufGoogle.Protobuf最小最快服务间通信、gRPC传输格式
MessagePackMessagePack-CSharp是(搭配AOT解析器)高吞吐量缓存、实时场景
JSONNewtonsoft.Json(依赖反射)最大较慢仅用于遗留项目 -- AOT场景请勿使用

When to Choose What

选型建议

  • System.Text.Json with source generators: Default choice for APIs, configuration, and any scenario where human-readable output or web client consumption matters. AOT-safe.
  • Protobuf: Default wire format for gRPC. Best throughput and smallest payload size for service-to-service communication. Schema-first development with
    .proto
    files.
  • MessagePack: When you need binary compactness without
    .proto
    schema management. Good for caching layers, real-time messaging, and high-throughput scenarios.

  • 带源生成器的System.Text.Json:API、配置,以及所有需要人类可读输出或供Web客户端消费场景的默认选择,兼容AOT。
  • Protobuf:gRPC的默认传输格式,服务间通信场景下吞吐量最高、payload体积最小,基于
    .proto
    文件的schema优先开发模式。
  • MessagePack:当你需要二进制紧凑格式但不想管理
    .proto
    schema时使用,适合缓存层、实时消息传输和高吞吐量场景。

Schema-Based vs Reflection-Based

基于Schema vs 基于反射的序列化

AspectSchema-BasedReflection-Based
ExamplesProtobuf, MessagePack, System.Text.Json (source gen)Newtonsoft.Json, BinaryFormatter
Type info in payloadNo (external schema)Yes (type names embedded)
VersioningExplicit field numbers/namesImplicit (type structure)
PerformanceFast (no reflection)Slower (runtime reflection)
AOT compatibleYesNo
Wire compatibilityExcellentPoor
Recommendation: Use schema-based serialization for anything that crosses process boundaries.
对比项基于Schema基于反射
示例Protobuf、MessagePack、System.Text.Json (源生成器)Newtonsoft.Json、BinaryFormatter
payload中的类型信息无(依赖外部schema)有(嵌入类型名称)
版本兼容性显式字段编号/名称隐式(依赖类型结构)
性能快(无反射)较慢(运行时反射)
AOT兼容
跨版本传输兼容性优秀
建议:所有跨进程边界的场景都使用基于Schema的序列化。

Formats to Avoid

不推荐使用的格式

FormatProblem
BinaryFormatterSecurity vulnerabilities, deprecated, never use
Newtonsoft.Json defaultType names in payload break on rename
DataContractSerializerComplex, poor versioning
XMLVerbose, slow, complex

格式问题
BinaryFormatter存在安全漏洞,已废弃,禁止使用
默认配置的Newtonsoft.Jsonpayload中的类型名称会在类重命名时失效
DataContractSerializer配置复杂,版本兼容性差
XML冗余度高、速度慢、配置复杂

System.Text.Json with Source Generators

搭配源生成器的System.Text.Json

For JSON serialization, use System.Text.Json with source generators for AOT compatibility and performance.
对于JSON序列化,请使用搭配源生成器的System.Text.Json来实现AOT兼容性和高性能。

Basic Setup

基础配置

csharp
using System.Text.Json.Serialization;

[JsonSerializable(typeof(Order))]
[JsonSerializable(typeof(List<Order>))]
[JsonSerializable(typeof(OrderStatus))]
public partial class AppJsonContext : JsonSerializerContext
{
}
csharp
using System.Text.Json.Serialization;

[JsonSerializable(typeof(Order))]
[JsonSerializable(typeof(List<Order>))]
[JsonSerializable(typeof(OrderStatus))]
public partial class AppJsonContext : JsonSerializerContext
{
}

Using the Generated Context

使用生成的上下文

csharp
// Serialize
string json = JsonSerializer.Serialize(order, AppJsonContext.Default.Order);

// Deserialize
Order? result = JsonSerializer.Deserialize(json, AppJsonContext.Default.Order);

// With options
var options = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    TypeInfoResolver = AppJsonContext.Default
};

string json = JsonSerializer.Serialize(order, options);
csharp
// 序列化
string json = JsonSerializer.Serialize(order, AppJsonContext.Default.Order);

// 反序列化
Order? result = JsonSerializer.Deserialize(json, AppJsonContext.Default.Order);

// 自定义配置
var options = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    TypeInfoResolver = AppJsonContext.Default
};

string json = JsonSerializer.Serialize(order, options);

ASP.NET Core Integration

ASP.NET Core集成

csharp
var builder = WebApplication.CreateBuilder(args);

// Minimal APIs
builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonContext.Default);
});

// MVC Controllers
builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonContext.Default);
    });
csharp
var builder = WebApplication.CreateBuilder(args);

// Minimal APIs
builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonContext.Default);
});

// MVC控制器
builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonContext.Default);
    });

Combining Multiple Contexts

合并多个上下文

csharp
builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolver = JsonTypeInfoResolver.Combine(
        AppJsonContext.Default,
        CatalogJsonContext.Default,
        InventoryJsonContext.Default
    );
});
csharp
builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolver = JsonTypeInfoResolver.Combine(
        AppJsonContext.Default,
        CatalogJsonContext.Default,
        InventoryJsonContext.Default
    );
});

Common Configuration

通用配置

csharp
[JsonSourceGenerationOptions(
    PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
    WriteIndented = false)]
[JsonSerializable(typeof(Order))]
[JsonSerializable(typeof(List<Order>))]
public partial class AppJsonContext : JsonSerializerContext
{
}
csharp
[JsonSourceGenerationOptions(
    PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
    WriteIndented = false)]
[JsonSerializable(typeof(Order))]
[JsonSerializable(typeof(List<Order>))]
public partial class AppJsonContext : JsonSerializerContext
{
}

Handling Polymorphism

处理多态

csharp
[JsonDerivedType(typeof(CreditCardPayment), "credit_card")]
[JsonDerivedType(typeof(BankTransferPayment), "bank_transfer")]
[JsonDerivedType(typeof(WalletPayment), "wallet")]
public abstract class Payment
{
    public decimal Amount { get; init; }
    public string Currency { get; init; } = "USD";
}

public class CreditCardPayment : Payment
{
    public string Last4Digits { get; init; } = "";
}

[JsonSerializable(typeof(Payment))]
public partial class AppJsonContext : JsonSerializerContext
{
}

csharp
[JsonDerivedType(typeof(CreditCardPayment), "credit_card")]
[JsonDerivedType(typeof(BankTransferPayment), "bank_transfer")]
[JsonDerivedType(typeof(WalletPayment), "wallet")]
public abstract class Payment
{
    public decimal Amount { get; init; }
    public string Currency { get; init; } = "USD";
}

public class CreditCardPayment : Payment
{
    public string Last4Digits { get; init; } = "";
}

[JsonSerializable(typeof(Payment))]
public partial class AppJsonContext : JsonSerializerContext
{
}

Protocol Buffers (Protobuf)

Protocol Buffers (Protobuf)

Best for: Actor systems, gRPC, event sourcing, any long-lived wire format.
适用场景:Actor系统、gRPC、事件溯源,所有需要长期稳定的传输格式场景。

Packages

依赖包

xml
<PackageReference Include="Google.Protobuf" Version="3.*" />
<PackageReference Include="Grpc.Tools" Version="2.*" PrivateAssets="All" />
xml
<PackageReference Include="Google.Protobuf" Version="3.*" />
<PackageReference Include="Grpc.Tools" Version="2.*" PrivateAssets="All" />

Proto File

Proto文件

protobuf
syntax = "proto3";

import "google/protobuf/timestamp.proto";

option csharp_namespace = "MyApp.Contracts";

message OrderMessage {
  int32 id = 1;
  string customer_id = 2;
  repeated OrderItemMessage items = 3;
  google.protobuf.Timestamp created_at = 4;
}

message OrderItemMessage {
  string product_id = 1;
  int32 quantity = 2;
  double unit_price = 3;
}
protobuf
syntax = "proto3";

import "google/protobuf/timestamp.proto";

option csharp_namespace = "MyApp.Contracts";

message OrderMessage {
  int32 id = 1;
  string customer_id = 2;
  repeated OrderItemMessage items = 3;
  google.protobuf.Timestamp created_at = 4;
}

message OrderItemMessage {
  string product_id = 1;
  int32 quantity = 2;
  double unit_price = 3;
}

Standalone Protobuf (Without gRPC)

独立使用Protobuf(无需gRPC)

csharp
using Google.Protobuf;

// Serialize to bytes
byte[] bytes = order.ToByteArray();

// Deserialize from bytes
var restored = OrderMessage.Parser.ParseFrom(bytes);

// Serialize to stream
using var stream = File.OpenWrite("order.bin");
order.WriteTo(stream);
csharp
using Google.Protobuf;

// 序列化为字节数组
byte[] bytes = order.ToByteArray();

// 从字节数组反序列化
var restored = OrderMessage.Parser.ParseFrom(bytes);

// 序列化为流
using var stream = File.OpenWrite("order.bin");
order.WriteTo(stream);

Proto File Registration in .csproj

.csproj中注册Proto文件

xml
<ItemGroup>
  <Protobuf Include="Protos\*.proto" GrpcServices="Both" />
</ItemGroup>
xml
<ItemGroup>
  <Protobuf Include="Protos\*.proto" GrpcServices="Both" />
</ItemGroup>

Versioning Rules

版本规则

protobuf
// SAFE: Add new fields with new numbers
message Order {
  string id = 1;
  string customer_id = 2;
  string shipping_address = 5;  // NEW - safe
}

// SAFE: Remove fields (keep the number reserved)
message Order {
  string id = 1;
  reserved 2;  // customer_id removed
}

// UNSAFE: Change field types
message Order {
  int32 id = 1;  // Was: string - BREAKS!
}

// UNSAFE: Reuse field numbers
message Order {
  reserved 2;
  string new_field = 2;  // Reusing 2 - BREAKS!
}

protobuf
// 安全:使用新编号添加新字段
message Order {
  string id = 1;
  string customer_id = 2;
  string shipping_address = 5;  // 新增 - 安全
}

// 安全:删除字段(保留编号占位)
message Order {
  string id = 1;
  reserved 2;  // customer_id已删除
}

// 不安全:修改字段类型
message Order {
  int32 id = 1;  // 原类型: string - 会破坏兼容性!
}

// 不安全:复用字段编号
message Order {
  reserved 2;
  string new_field = 2;  // 复用编号2 - 会破坏兼容性!
}

MessagePack

MessagePack

Best for: High-performance scenarios, compact payloads, actor messaging.
适用场景:高性能场景、紧凑payload、Actor消息传输。

Packages

依赖包

xml
<PackageReference Include="MessagePack" Version="3.*" />
<PackageReference Include="MessagePack.SourceGenerator" Version="3.*" />
xml
<PackageReference Include="MessagePack" Version="3.*" />
<PackageReference Include="MessagePack.SourceGenerator" Version="3.*" />

Basic Usage with Source Generator (AOT-Safe)

源生成器基础用法(兼容AOT)

csharp
using MessagePack;

[MessagePackObject]
public partial class Order
{
    [Key(0)]
    public int Id { get; init; }

    [Key(1)]
    public string CustomerId { get; init; } = "";

    [Key(2)]
    public List<OrderItem> Items { get; init; } = [];

    [Key(3)]
    public DateTimeOffset CreatedAt { get; init; }

    [Key(4)]
    public string? Notes { get; init; }
}
csharp
using MessagePack;

[MessagePackObject]
public partial class Order
{
    [Key(0)]
    public int Id { get; init; }

    [Key(1)]
    public string CustomerId { get; init; } = "";

    [Key(2)]
    public List<OrderItem> Items { get; init; } = [];

    [Key(3)]
    public DateTimeOffset CreatedAt { get; init; }

    [Key(4)]
    public string? Notes { get; init; }
}

Serialization

序列化

csharp
// Serialize
byte[] bytes = MessagePackSerializer.Serialize(order);

// Deserialize
var restored = MessagePackSerializer.Deserialize<Order>(bytes);

// With compression (LZ4)
var lz4Options = MessagePackSerializerOptions.Standard.WithCompression(
    MessagePackCompression.Lz4BlockArray);
byte[] compressed = MessagePackSerializer.Serialize(order, lz4Options);
csharp
// 序列化
byte[] bytes = MessagePackSerializer.Serialize(order);

// 反序列化
var restored = MessagePackSerializer.Deserialize<Order>(bytes);

// 开启LZ4压缩
var lz4Options = MessagePackSerializerOptions.Standard.WithCompression(
    MessagePackCompression.Lz4BlockArray);
byte[] compressed = MessagePackSerializer.Serialize(order, lz4Options);

AOT Resolver Setup

AOT解析器配置

csharp
MessagePackSerializer.DefaultOptions = MessagePackSerializerOptions.Standard
    .WithResolver(GeneratedResolver.Instance);

csharp
MessagePackSerializer.DefaultOptions = MessagePackSerializerOptions.Standard
    .WithResolver(GeneratedResolver.Instance);

Wire Compatibility Patterns

传输兼容性最佳实践

Tolerant Reader

宽容读取

Old code must safely ignore unknown fields:
csharp
// Protobuf/MessagePack: Automatic - unknown fields skipped
// System.Text.Json: Configure to allow
var options = new JsonSerializerOptions
{
    UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip
};
旧版本代码必须能够安全忽略未知字段:
csharp
// Protobuf/MessagePack:自动忽略未知字段
// System.Text.Json:手动配置开启
var options = new JsonSerializerOptions
{
    UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip
};

Introduce Read Before Write

先实现读能力再实现写能力

Deploy deserializers before serializers for new formats:
csharp
// Phase 1: Add deserializer (deployed everywhere)
public Order Deserialize(byte[] data, string manifest) => manifest switch
{
    "Order.V1" => DeserializeV1(data),
    "Order.V2" => DeserializeV2(data),  // NEW - can read V2
    _ => throw new NotSupportedException()
};

// Phase 2: Enable serializer (after V1 deployed everywhere)
public (byte[] data, string manifest) Serialize(Order order) =>
    _useV2Format
        ? (SerializeV2(order), "Order.V2")
        : (SerializeV1(order), "Order.V1");
部署新格式时,先全量部署反序列化逻辑,再部署序列化逻辑:
csharp
// 阶段1:添加反序列化逻辑(全量部署)
public Order Deserialize(byte[] data, string manifest) => manifest switch
{
    "Order.V1" => DeserializeV1(data),
    "Order.V2" => DeserializeV2(data),  // 新增:可读取V2格式
    _ => throw new NotSupportedException()
};

// 阶段2:启用序列化逻辑(V1反序列化全量部署后开启)
public (byte[] data, string manifest) Serialize(Order order) =>
    _useV2Format
        ? (SerializeV2(order), "Order.V2")
        : (SerializeV1(order), "Order.V1");

Never Embed Type Names

不要嵌入类型名称

csharp
// BAD: Type name in payload - renaming class breaks wire format
{
    "$type": "MyApp.Order, MyApp",
    "id": 123
}

// GOOD: Explicit discriminator - refactoring safe
{
    "type": "order",
    "id": 123
}

csharp
// 错误:payload中携带类型名称 - 类重命名会破坏传输兼容性
{
    "$type": "MyApp.Order, MyApp",
    "id": 123
}

// 正确:显式类型标识 - 重构安全
{
    "type": "order",
    "id": 123
}

Performance Comparison

性能对比

Approximate throughput (higher is better):
FormatSerializeDeserializeSize
MessagePack★★★★★★★★★★★★★★★
Protobuf★★★★★★★★★★★★★★★
System.Text.Json (source gen)★★★★☆★★★★☆★★★☆☆
System.Text.Json (reflection)★★★☆☆★★★☆☆★★★☆☆
Newtonsoft.Json★★☆☆☆★★☆☆☆★★★☆☆
近似吞吐量(越高越好):
格式序列化性能反序列化性能体积
MessagePack★★★★★★★★★★★★★★★
Protobuf★★★★★★★★★★★★★★★
System.Text.Json (源生成器)★★★★☆★★★★☆★★★☆☆
System.Text.Json (反射)★★★☆☆★★★☆☆★★★☆☆
Newtonsoft.Json★★☆☆☆★★☆☆☆★★★☆☆

Optimization Tips

优化建议

  • Reuse
    JsonSerializerOptions
    -- creating options is expensive
  • Use
    JsonSerializerContext
    -- eliminates warm-up cost
  • Use
    Utf8JsonWriter
    /
    Utf8JsonReader
    for streaming scenarios
  • Use Protobuf
    ByteString
    for binary data instead of base64-encoded strings
  • Enable MessagePack LZ4 compression for large payloads

  • 复用
    JsonSerializerOptions
    -- 创建Options实例的开销很高
  • 使用
    JsonSerializerContext
    -- 消除预热成本
  • 流式场景下使用
    Utf8JsonWriter
    /
    Utf8JsonReader
  • 二进制数据使用Protobuf
    ByteString
    ,而非base64编码的字符串
  • 大payload启用MessagePack LZ4压缩

Anti-Patterns: Reflection-Based Serialization

反模式:基于反射的序列化

Do not use reflection-based serializers in Native AOT or trimming scenarios.
禁止在Native AOT或裁剪场景下使用基于反射的序列化器。

Newtonsoft.Json (JsonConvert)

Newtonsoft.Json (JsonConvert)

csharp
// BAD: Reflection-based -- fails under AOT/trimming
var json = JsonConvert.SerializeObject(order);
var order = JsonConvert.DeserializeObject<Order>(json);

// GOOD: Source-generated -- AOT-safe
var json = JsonSerializer.Serialize(order, AppJsonContext.Default.Order);
var order = JsonSerializer.Deserialize(json, AppJsonContext.Default.Order);
csharp
// 错误:基于反射 -- AOT/裁剪场景下会失败
var json = JsonConvert.SerializeObject(order);
var order = JsonConvert.DeserializeObject<Order>(json);

// 正确:源生成 -- 兼容AOT
var json = JsonSerializer.Serialize(order, AppJsonContext.Default.Order);
var order = JsonSerializer.Deserialize(json, AppJsonContext.Default.Order);

System.Text.Json Without Source Generators

未使用源生成器的System.Text.Json

csharp
// BAD: No context -- uses runtime reflection
var json = JsonSerializer.Serialize(order);

// GOOD: Explicit context -- uses source-generated code
var json = JsonSerializer.Serialize(order, AppJsonContext.Default.Order);
csharp
// 错误:未指定上下文 -- 会使用运行时反射
var json = JsonSerializer.Serialize(order);

// 正确:显式指定上下文 -- 使用源生成的代码
var json = JsonSerializer.Serialize(order, AppJsonContext.Default.Order);

Migration Path from Newtonsoft.Json

从Newtonsoft.Json迁移路径

  1. Replace
    JsonConvert.SerializeObject
    /
    DeserializeObject
    with
    JsonSerializer.Serialize
    /
    Deserialize
  2. Replace
    [JsonProperty]
    with
    [JsonPropertyName]
  3. Replace
    JsonConverter
    base class with
    JsonConverter<T>
    from System.Text.Json
  4. Create a
    JsonSerializerContext
    with
    [JsonSerializable]
    for all serialized types
  5. Replace
    JObject
    /
    JToken
    dynamic access with
    JsonDocument
    /
    JsonElement
    or strongly-typed models
  6. Test serialization round-trips -- attribute semantics differ

  1. JsonConvert.SerializeObject
    /
    DeserializeObject
    替换为
    JsonSerializer.Serialize
    /
    Deserialize
  2. [JsonProperty]
    替换为
    [JsonPropertyName]
  3. 将JsonConverter基类替换为System.Text.Json提供的
    JsonConverter<T>
  4. 为所有需要序列化的类型添加
    [JsonSerializable]
    注解,创建
    JsonSerializerContext
  5. JObject
    /
    JToken
    动态访问替换为
    JsonDocument
    /
    JsonElement
    或者强类型模型
  6. 测试序列化往返 -- 注解语义存在差异

Akka.NET Serialization

Akka.NET序列化

For Akka.NET actor systems, use schema-based serialization:
hocon
akka {
  actor {
    serializers {
      messagepack = "Akka.Serialization.MessagePackSerializer, Akka.Serialization.MessagePack"
    }
    serialization-bindings {
      "MyApp.Messages.IMessage, MyApp" = messagepack
    }
  }
}

对于Akka.NET Actor系统,使用基于Schema的序列化:
hocon
akka {
  actor {
    serializers {
      messagepack = "Akka.Serialization.MessagePackSerializer, Akka.Serialization.MessagePack"
    }
    serialization-bindings {
      "MyApp.Messages.IMessage, MyApp" = messagepack
    }
  }
}

Key Principles

核心原则

  • Default to System.Text.Json with source generators for all JSON serialization
  • Use Protobuf for service-to-service binary serialization
  • Use MessagePack for high-throughput caching and real-time
  • Never use Newtonsoft.Json for new AOT-targeted projects
  • Always register
    JsonSerializerContext
    in ASP.NET Core
  • Annotate all serialized types -- source generators only generate code for listed types

  • 所有JSON序列化默认使用带源生成器的System.Text.Json
  • 服务间二进制序列化使用Protobuf
  • 高吞吐量缓存和实时场景使用MessagePack
  • 面向AOT的新项目禁止使用Newtonsoft.Json
  • ASP.NET Core中务必注册
    JsonSerializerContext
  • 所有序列化类型都要添加注解 -- 源生成器只会为显式列出的类型生成代码

Agent Gotchas

注意事项

  1. Do not use
    JsonSerializer.Serialize(obj)
    without a context in AOT projects
    -- it falls back to reflection.
  2. Do not forget to list collection types in
    [JsonSerializable]
    --
    [JsonSerializable(typeof(Order))]
    does not cover
    List<Order>
    .
  3. Do not use Newtonsoft.Json
    [JsonProperty]
    attributes with System.Text.Json
    -- they are silently ignored.
  4. Do not mix MessagePack
    [Key]
    integer keys with
    [Key]
    string keys
    in the same type hierarchy.
  5. Do not omit
    GrpcServices
    attribute on
    <Protobuf>
    items
    -- without it, both client and server stubs are generated.

  1. AOT项目中不要不带上下文直接使用
    JsonSerializer.Serialize(obj)
    -- 会回退到反射实现
  2. 不要忘记在
    [JsonSerializable]
    中列出集合类型
    --
    [JsonSerializable(typeof(Order))]
    不会覆盖
    List<Order>
  3. 不要在System.Text.Json中使用Newtonsoft.Json的
    [JsonProperty]
    注解
    -- 会被静默忽略
  4. 同一类型层级中不要混用MessagePack的
    [Key]
    整数键和
    [Key]
    字符串键
  5. 不要省略
    <Protobuf>
    节点的
    GrpcServices
    属性
    -- 不配置的话会同时生成客户端和服务端存根

Resources

参考资源