using System.Globalization;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
namespace PasteApeart.Handler
{
/// <summary>
///
/// </summary>
public static class JsonConfig
{
static JsonConfig()
{
Options.Converters.Add(new SafeStringConverter());
Options.Converters.Add(new SafeDateTimeConverter());
//Options.Converters.Add(new SafeEnumConverter<EnumRoleType>());
//Options.Converters.Add(new SafeNullableDateTimeConverter());
//SafeNullableDateTimeConverter
}
/// <summary>
///
/// </summary>
public static readonly JsonSerializerOptions Options = new()
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
NumberHandling = JsonNumberHandling.AllowReadingFromString,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
ReferenceHandler = ReferenceHandler.IgnoreCycles, // 忽略循环引用
// 关键在这里:组合解析器会自动查找
// 先在 MyJsonContext 找,找不到自动用反射
TypeInfoResolver = JsonTypeInfoResolver.Combine(
//PasteSpiderJsonContext.Default, // 源生成器部分
new DefaultJsonTypeInfoResolver() // 反射兜底
)
};
}
/// <summary>
///
/// </summary>
public class SafeStringConverter : JsonConverter<string>
{
/// <summary>
///
/// </summary>
/// <param name="reader"></param>
/// <param name="typeToConvert"></param>
/// <param name="options"></param>
/// <returns></returns>
/// <exception cref="JsonException"></exception>
public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
switch (reader.TokenType)
{
case JsonTokenType.String:
return reader.GetString();
case JsonTokenType.Number:
// 方法1:读取为 long(可能溢出)
if (reader.TryGetInt64(out long longValue))
{
return longValue.ToString();
}
// 方法2:读取为 decimal(范围更大)
if (reader.TryGetDecimal(out decimal decimalValue))
{
return decimalValue.ToString();
}
// 方法3:读取为 double(可能丢失精度)
return reader.GetDouble().ToString();
case JsonTokenType.Null:
return null;
case JsonTokenType.True:
return "true";
case JsonTokenType.False:
return "false";
default:
throw new JsonException($"Cannot convert {reader.TokenType} to string");
}
}
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
{
writer.WriteStringValue(value);
}
}
public class SafeDateTimeConverter : JsonConverter<DateTime>
{
// 支持的常见日期时间格式
private static readonly string[] _supportedFormats = new[]
{
"yyyy-MM-dd HH:mm:ss",
"yyyy-MM-dd HH:mm:ss.fff",
"yyyy-MM-ddTHH:mm:ss",
"yyyy-MM-ddTHH:mm:ss.fff",
"yyyy-MM-dd HH:mm",
"yyyy-MM-dd",
"yyyy/MM/dd HH:mm:ss",
"yyyy/MM/dd HH:mm:ss.fff",
"yyyy/MM/dd",
"dd/MM/yyyy HH:mm:ss",
"dd/MM/yyyy HH:mm:ss.fff",
"dd/MM/yyyy"
};
// 关键改动:在不变模式下使用的备用 CultureInfo
private static readonly CultureInfo _safeCulture;
static SafeDateTimeConverter()
{
try
{
// 尝试获取 InvariantCulture(正常模式)
_safeCulture = CultureInfo.InvariantCulture;
}
catch
{
// 不变模式下,InvariantCulture 不可用,使用固定中文
_safeCulture = new CultureInfo("zh-CN");
}
}
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.String)
{
string value = reader.GetString();
if (string.IsNullOrWhiteSpace(value))
{
throw new JsonException("日期时间不能为空");
}
// 尝试使用指定格式解析
foreach (string format in _supportedFormats)
{
if (DateTime.TryParseExact(value, format,
_safeCulture, // 使用安全的文化
DateTimeStyles.None,
out DateTime date))
{
return date;
}
}
// 最后尝试使用系统默认解析(兜底)
if (DateTime.TryParse(value, _safeCulture, DateTimeStyles.None, out DateTime result))
{
return result;
}
// 都失败了,抛出明确异常
throw new JsonException($"无法将 '{value}' 转换为 DateTime。支持的格式包括: {string.Join(", ", _supportedFormats)}");
}
// 处理数字时间戳
if (reader.TokenType == JsonTokenType.Number)
{
long timestamp = reader.GetInt64();
return DateTimeOffset.FromUnixTimeSeconds(timestamp).DateTime;
}
throw new JsonException($"意外的 token 类型: {reader.TokenType},期望字符串或数字");
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
// 输出使用标准格式
writer.WriteStringValue(value.ToString("yyyy-MM-dd HH:mm:ss", _safeCulture));
}
}
/// <summary>
/// 枚举转换器 - 反序列化支持字符串数字/数字/枚举名,序列化输出数字
/// </summary>
public class SafeEnumConverter<T> : JsonConverter<T> where T : struct, Enum
{
/// <summary>
///
/// </summary>
/// <param name="reader"></param>
/// <param name="typeToConvert"></param>
/// <param name="options"></param>
/// <returns></returns>
/// <exception cref="JsonException"></exception>
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
switch (reader.TokenType)
{
case JsonTokenType.String:
string stringValue = reader.GetString();
// 1. 尝试按枚举名称解析(不区分大小写)
if (Enum.TryParse<T>(stringValue, true, out T enumValue))
return enumValue;
// 2. 尝试按数字字符串解析(如 "1", "2")
if (int.TryParse(stringValue, out int intValue))
{
if (Enum.IsDefined(typeof(T), intValue))
return (T)(object)intValue;
}
break;
case JsonTokenType.Number:
// 直接的数字值(如 1,2,3)
int numValue = reader.GetInt32();
if (Enum.IsDefined(typeof(T), numValue))
return (T)(object)numValue;
break;
case JsonTokenType.Null:
return default;
}
throw new JsonException($"Cannot convert '{reader.TokenType}' to enum type '{typeof(T).Name}'");
}
/// <summary>
///
/// </summary>
/// <param name="writer"></param>
/// <param name="value"></param>
/// <param name="options"></param>
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
// 关键:序列化时输出数字值,而不是名称
writer.WriteNumberValue(Convert.ToInt32(value));
}
}
}