Sage.JsonConverters 2.0.0

dotnet add package Sage.JsonConverters --version 2.0.0
                    
NuGet\Install-Package Sage.JsonConverters -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Sage.JsonConverters" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Sage.JsonConverters" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Sage.JsonConverters" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Sage.JsonConverters --version 2.0.0
                    
#r "nuget: Sage.JsonConverters, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Sage.JsonConverters@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Sage.JsonConverters&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Sage.JsonConverters&version=2.0.0
                    
Install as a Cake Tool

Sage.JsonConverters

Sage.JsonConverters 是一个面向 System.Text.Json 的轻量级安全转换器库。

它专注于一件事:提供一组可按需注册的基础类型转换器,在第三方接口返回空字符串、字符串数字、字符串布尔值或不够规范的时间字符串时,尽量避免反序列化异常。

设计目标

  • 轻量:只提供基础转换器,不内置库级全局配置工厂。
  • 简单:所有能力都围绕显式注册 converter 展开。
  • 高效:优先使用 TryParse / TryGetXxx 路径,避免不必要的异常流程。
  • AOT 友好:不依赖对象转换反射工厂,也不依赖运行时动态代码生成逻辑。

安装

dotnet add package Sage.JsonConverters

快速开始

最常见的方式是在局部创建 JsonSerializerOptions,只注册当前模型需要的转换器。

using Sage.JsonConverters;
using System.Text.Json;

var options = new JsonSerializerOptions
{
    PropertyNameCaseInsensitive = true
};

options.Converters.Add(new SafeJsonConverters.SafeIntConverter());
options.Converters.Add(new SafeJsonConverters.SafeBoolConverter());
options.Converters.Add(new SafeJsonConverters.SafeNullableDateTimeOffsetConverter());

var payload = JsonSerializer.Deserialize<OrderPayload>(
    """
    {
        "id": "123",
        "isPaid": "true",
        "changedAt": ""
    }
    """,
    options);

public sealed class OrderPayload
{
    public int Id { get; set; }

    public bool IsPaid { get; set; }

    public DateTimeOffset? ChangedAt { get; set; }
}

上面的行为如下:

  • "123" 会被解析为 123
  • "true" 会被解析为 true
  • "" 会被解析为 null

常见注册方式

1. 局部 JsonSerializerOptions

这是最推荐的方式,影响范围最小,也最容易控制具体模型的兼容规则。

using Sage.JsonConverters;
using System.Text.Json;

var options = new JsonSerializerOptions
{
    PropertyNameCaseInsensitive = true
};

options.Converters.Add(new SafeJsonConverters.SafeNullableIntConverter());
options.Converters.Add(new SafeJsonConverters.SafeNullableDecimalConverter());
options.Converters.Add(new SafeJsonConverters.SafeNullableBoolConverter());
options.Converters.Add(new SafeJsonConverters.SafeNullableDateTimeOffsetConverter());

var payload = JsonSerializer.Deserialize<InvoicePayload>(
    """
    {
        "id": "123",
        "amount": "99.95",
        "isPaid": "true",
        "changedAt": ""
    }
    """,
    options);

public sealed class InvoicePayload
{
    public int? Id { get; set; }

    public decimal? Amount { get; set; }

    public bool? IsPaid { get; set; }

    public DateTimeOffset? ChangedAt { get; set; }
}

2. 按属性标注转换器

如果只有少数字段需要放宽反序列化规则,可以直接在属性上标注 [JsonConverter]

using Sage.JsonConverters;
using System.Text.Json.Serialization;

public sealed class ProductPayload
{
    [JsonConverter(typeof(SafeJsonConverters.SafeIntConverter))]
    public int Id { get; set; }

    [JsonConverter(typeof(SafeJsonConverters.SafeNullableDecimalConverter))]
    public decimal? Price { get; set; }

    [JsonConverter(typeof(SafeJsonConverters.SafeNullableDateTimeOffsetConverter))]
    public DateTimeOffset? ChangedAt { get; set; }
}

3. 业务项目自己封装常用配置

库本身不提供 Create*Options() 工厂方法,但业务项目可以按自己的边界封装一层。

using Sage.JsonConverters;
using System.Text.Json;

public static class ApiJsonOptions
{
    public static JsonSerializerOptions Create()
    {
        var options = new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        };

        options.Converters.Add(new SafeJsonConverters.SafeNullableIntConverter());
        options.Converters.Add(new SafeJsonConverters.SafeNullableBoolConverter());
        options.Converters.Add(new SafeJsonConverters.SafeNullableDateTimeOffsetConverter());

        return options;
    }
}

4. Web API 全局配置

虽然库本身不内置全局配置入口,但你可以在 ASP.NET Core / Web API 项目里手动全局注册这些 converter。

using Sage.JsonConverters;
using System.Text.Json;
using System.Text.Json.Serialization;

builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
    options.SerializerOptions.PropertyNameCaseInsensitive = true;
    options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
    options.SerializerOptions.WriteIndented = builder.Environment.IsDevelopment();
    options.SerializerOptions.IncludeFields = true;
    options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;

    options.SerializerOptions.Converters.Add(new SafeJsonConverters.SafeNullableDateTimeOffsetConverter());
    options.SerializerOptions.Converters.Add(new SafeJsonConverters.SafeNullableIntConverter());
    options.SerializerOptions.Converters.Add(new SafeJsonConverters.SafeNullableBoolConverter());
});

全局注册适合统一处理来自外部系统的脏数据,但它会影响整个 Web API 的输入行为。对于只在少数字段上需要放宽规则的场景,仍然更推荐属性标注或局部 options。

AOT / Source Generation

库中的转换器本身不依赖反射工厂或动态代码生成,可以安全用于 AOT 场景。

如果你的业务模型使用 System.Text.Json 源生成,推荐直接在对应属性上标注标准的 [JsonConverter(typeof(...))] 特性,让源生成元数据在编译期就固定下来。

using Sage.JsonConverters;
using System.Text.Json;
using System.Text.Json.Serialization;

public sealed class EventPayload
{
    [JsonConverter(typeof(SafeJsonConverters.SafeNullableIntConverter))]
    public int? Id { get; set; }

    [JsonConverter(typeof(SafeJsonConverters.SafeNullableDateTimeOffsetConverter))]
    public DateTimeOffset? ChangedAt { get; set; }
}

[JsonSerializable(typeof(EventPayload))]
internal partial class AppJsonSerializerContext : JsonSerializerContext
{
}

var payload = JsonSerializer.Deserialize(
    """{"Id":"123","ChangedAt":""}""",
    AppJsonSerializerContext.Default.EventPayload);

支持的转换器

数值

  • SafeIntConverter
  • SafeNullableIntConverter
  • SafeLongConverter
  • SafeNullableLongConverter
  • SafeDoubleConverter
  • SafeNullableDoubleConverter
  • SafeDecimalConverter
  • SafeNullableDecimalConverter

规则概览:

  • 字符串数字使用 InvariantCulture 解析
  • 无效输入返回默认值或 null
  • 超范围数值会按目标类型进行边界收敛

示例:

  • SafeIntConverter: "123"123""0
  • SafeNullableIntConverter: "123"123""null
  • SafeDoubleConverter: "12.5"12.5
  • SafeNullableDecimalConverter: "99.95"99.95m

布尔

  • SafeBoolConverter
  • SafeNullableBoolConverter

支持的字符串值包括:

  • true / false
  • 1 / 0
  • yes / no
  • on / off
  • y / n

示例:

  • SafeBoolConverter: "yes"true
  • SafeNullableBoolConverter: ""null

时间

  • SafeDateTimeConverter
  • SafeNullableDateTimeConverter
  • SafeDateTimeOffsetConverter
  • SafeNullableDateTimeOffsetConverter

规则概览:

  • null、空字符串、空白字符串会返回默认值或 null
  • 仅支持固定的、无歧义的日期格式与 round-trip 格式
  • 不再接受 MM/dd/yyyydd/MM/yyyy 这类有歧义的格式
  • 序列化时使用 Utf8JsonWriter 内置的 ISO 8601 写法,保留时区与小数秒信息
  • DateTimeOffset 无时区输入按 UTC 解释

示例:

  • SafeNullableDateTimeConverter: ""null
  • SafeNullableDateTimeOffsetConverter: ""null
  • SafeDateTimeOffsetConverter: "2024-01-02T03:04:05+08:00" → 对应 DateTimeOffset

2.0 迁移指引

2.0.0 包含以下 breaking changes:

  • 删除所有 Create*Options() 预设方法
  • 删除静态单例字段,改为显式 new converter
  • 删除 SafeObjectConverter<T> 与对象转换工厂
  • 新增 DateTimeOffset / DateTimeOffset? 支持
  • DateTime 不再接受有歧义的日期格式

如果你从 1.x 升级到 2.0,通常需要这样调整:

  • 旧写法:SafeJsonConverters.NullableInt

  • 新写法:new SafeJsonConverters.SafeNullableIntConverter()

  • 旧写法:SafeJsonConverters.CreateSafeOptions()

  • 新写法:在业务侧手动创建 JsonSerializerOptions 并按需注册 converter

许可证

Apache 2.0

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0 108 4/24/2026
1.0.0.3 237 10/13/2025
1.0.0.2 153 10/4/2025
1.0.0.1 216 10/2/2025
1.0.0 221 9/30/2025

2.0.0: 移除预设 JsonSerializerOptions 与对象转换器,新增 DateTimeOffset 支持,并统一为按需注册的使用方式。