You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

复用JsonSerializerOptions遇只读/已使用异常,如何检测?

复用JsonSerializerOptions时的只读状态检测问题

我尝试在多处复用JsonSerializerOptions,编写了如下代码:

public static void ConfigureJsonSerializerOptions(JsonSerializerOptions jsonSerializerOptions)
{
    jsonSerializerOptions.PropertyNameCaseInsensitive = true;
    jsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
    jsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
    jsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
    jsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString;
    jsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip;
    jsonSerializerOptions.UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode;
    jsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
    jsonSerializerOptions.AllowTrailingCommas = true;
    jsonSerializerOptions.WriteIndented = true;
}

private static JsonSerializerOptions options;

public static JsonSerializerOptions Options
{
    get
    {
        if (options == null)
        {
            options = new JsonSerializerOptions();
            ConfigureJsonSerializerOptions(options);
            options.Converters.Add(new JsonStringEnumConverter());
        }
        return options;
    }
}

但运行时抛出异常:

An exception of type 'System.InvalidOperationException' occurred in System.Text.Json.dll but was not handled in user code: 'This JsonSerializerOptions instance is read-only or has already been used in serialization or deserialization.'

我想知道如何检测该状态,因为JsonSerializerOptions实例并没有IsReadOnly属性。


问题原因

JsonSerializerOptions实例在第一次用于序列化或反序列化操作后,会被框架内部标记为只读,此时再修改它的属性或添加转换器就会抛出上述异常。官方并未提供公开的IsReadOnly属性来直接检测这个状态,但可以通过以下几种方式实现检测:

1. 捕获异常检测

通过尝试修改一个不影响业务的属性(比如临时切换WriteIndented的值),如果抛出InvalidOperationException,则说明实例已处于只读状态:

public static bool IsJsonOptionsReadOnly(JsonSerializerOptions options)
{
    try
    {
        // 临时修改属性再还原,测试是否可写
        var originalValue = options.WriteIndented;
        options.WriteIndented = !originalValue;
        options.WriteIndented = originalValue;
        return false;
    }
    catch (InvalidOperationException)
    {
        return true;
    }
}

2. 自行维护状态标记

在代码中手动记录JsonSerializerOptions是否已被使用。比如在第一次调用序列化/反序列化的地方标记一个布尔变量,后续修改前先检查这个标记:

private static JsonSerializerOptions _options;
private static bool _optionsHasBeenUsed;

public static JsonSerializerOptions Options
{
    get
    {
        if (_options == null)
        {
            _options = new JsonSerializerOptions();
            ConfigureJsonSerializerOptions(_options);
            _options.Converters.Add(new JsonStringEnumConverter());
        }
        return _options;
    }
}

// 调用序列化/反序列化时标记状态
public static T Deserialize<T>(string json)
{
    _optionsHasBeenUsed = true;
    return JsonSerializer.Deserialize<T>(json, Options);
}

// 检测方法
public static bool IsOptionsReadOnly() => _optionsHasBeenUsed;

3. 反射读取内部字段(不推荐)

通过反射读取JsonSerializerOptions内部的_isReadOnly字段,但这种方式依赖框架内部实现,可能随.NET版本更新而失效,不建议在生产环境使用:

using System.Reflection;

public static bool IsJsonOptionsReadOnly(JsonSerializerOptions options)
{
    var readOnlyField = typeof(JsonSerializerOptions)
        .GetField("_isReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
    
    return readOnlyField != null && (bool)readOnlyField.GetValue(options);
}

优化建议

为了彻底避免这类异常,建议确保JsonSerializerOptions在初始化完成后不再被修改。可以使用Lazy<T>实现线程安全的懒加载,确保配置只初始化一次:

public static void ConfigureJsonSerializerOptions(JsonSerializerOptions jsonSerializerOptions)
{
    jsonSerializerOptions.PropertyNameCaseInsensitive = true;
    jsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
    jsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
    jsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
    jsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString;
    jsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip;
    jsonSerializerOptions.UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode;
    jsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
    jsonSerializerOptions.AllowTrailingCommas = true;
    jsonSerializerOptions.WriteIndented = true;
    jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
}

private static readonly Lazy<JsonSerializerOptions> _lazyOptions = new Lazy<JsonSerializerOptions>(() =>
{
    var options = new JsonSerializerOptions();
    ConfigureJsonSerializerOptions(options);
    return options;
});

public static JsonSerializerOptions Options => _lazyOptions.Value;

内容的提问来源于stack exchange,提问作者Big boy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 11:40:33