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

Blazor WASM JS Interop序列化问题:C# object属性仅返回JsonElement

问题:JS任意类型序列化为C#对象时object属性变为JsonElement的原因及解决方案

我尝试将JS中的任意类型序列化为C#中的对象,但始终无法正常工作。我的EventAction类拥有以下属性:

public DotNetObjectReference<EventAction<T>> DotNetObjRef { get; }

该属性用于从JS代码调用事件,示例如下:

function onCellValueChanged(data, handler) {
    var mapped = {
        columnName: data.column.colId, // string
        rowIndex: data.rowIndex, // number
        oldValue: data.oldValue, // any
        newValue: data.newValue, // any
    };
    handler.dotNetObjRef.invokeMethodAsync('Invoke', mapped);
}

调用方法本身运行正常,但序列化仅部分生效。示例中我定义了CellValueChangedArgs类:

public class CellValueChangedArgs
{
    public string ColumnName { get; set; } = string.Empty;
    public int RowIndex { get; set; }
    public object? OldValue { get; set; }
    public object? NewValue { get; set; }
}

当生成该类的实例时,string和int类型的属性可正常序列化,但object?类型的OldValue和NewValue并未得到实际值(如字符串、数字、日期等),而是JsonElement实例。若将这两个属性的类型改为string,则序列化可正常进行。

请问出现该问题的原因是什么?将其序列化为字符串再手动转换是否为合适的解决方案?


原因分析

  • .NET默认的System.Text.Json序列化器处理object类型属性时,无法提前推断出该属性对应的具体CLR类型,因此会将JSON值保留为JsonElement类型(这是JSON DOM的基本元素,用于存储原始JSON的结构和值)。
  • 对于string、int这类类型明确的属性,序列化器可以直接匹配JSON中的对应类型,所以能正确完成反序列化。

解决方案评估

1. 序列化为字符串再手动转换:可行但不推荐

这种方法确实能解决问题,但需要手动编写各种类型的转换逻辑,比如判断值是数字、日期还是字符串,再做对应转换。不仅代码冗余,还容易出现类型转换错误,维护成本较高,只适合临时的简单场景。

2. 更优的替代方案

  • 泛型类改造:如果能提前确定OldValue/NewValue的类型,将CellValueChangedArgs改为泛型类:
    public class CellValueChangedArgs<T>
    {
        public string ColumnName { get; set; } = string.Empty;
        public int RowIndex { get; set; }
        public T? OldValue { get; set; }
        public T? NewValue { get; set; }
    }
    
    序列化器可以明确目标类型,自动完成反序列化,同时保留静态类型检查的优势。
  • 自定义Json转换器:编写继承自JsonConverter<object>的转换器,根据JsonElement的ValueKind判断原始类型,转换为对应的CLR类型(如int、string、DateTime等)。示例如下:
    public class ObjectJsonConverter : JsonConverter<object>
    {
        public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            return reader.TokenType switch
            {
                JsonTokenType.String => reader.GetString(),
                JsonTokenType.Number => reader.TryGetInt64(out long l) ? l : reader.GetDouble(),
                JsonTokenType.True => true,
                JsonTokenType.False => false,
                JsonTokenType.Null => null,
                _ => JsonDocument.ParseValue(ref reader).RootElement.Clone()
            };
        }
    
        public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
        {
            JsonSerializer.Serialize(writer, value, value.GetType(), options);
        }
    }
    
    使用时在JsonSerializerOptions中添加该转换器,或者给object属性标记[JsonConverter(typeof(ObjectJsonConverter))],即可自动处理任意类型的反序列化。
  • 使用dynamic类型:将OldValue/NewValue改为dynamic类型,这样在使用时可以直接调用对应类型的方法,但会失去静态类型检查,运行时可能出现类型错误,适合快速迭代的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 00:33:10