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

C# .NET Core反序列化POST JSON数据遇SerializationException错误

解决DataContractJsonSerializer反序列化JSON时的SerializationException问题

嘿,我刚碰过类似的坑,你遇到的这个SerializationException根源其实很明确——DataContractJsonSerializer搞不定MongoDB的ObjectId类型,再加上你传的JSON里_id是字符串格式,和模型里定义的ObjectId类型不匹配,直接触发了序列化器的错误。

问题拆解

你的UserModel里_id被定义成ObjectId类型,但POST过来的JSON里它是个纯字符串(比如"8kmXH1fzSrVS8PqNLMwyhRH4hBw1")。DataContractJsonSerializer是个比较老旧的组件,默认根本不知道怎么把字符串转成ObjectId对象,所以它直接懵了,抛出了那个"期望Element却遇到Text"的错误——它本来等着要符合数据契约结构的元素,结果拿到的是一段纯文本。

另外提一句,你给的JSON示例里缺了registeredAt字段,不过这个是小事,DataMember默认是可选的,不会直接导致这个报错,但最好补上或者给模型里的这个字段加个默认值。

可行的解决方案

这里给你几个方案,按易用程度排序:

方案1:换用Newtonsoft.Json(Json.NET)反序列化

真心建议你换掉DataContractJsonSerializer,这个组件太老了,对复杂类型的支持远不如Json.NET。换了之后处理ObjectId简直丝滑:

先确保项目引用了Newtonsoft.Json包,然后修改控制器代码:

using Newtonsoft.Json;

public string RequestToken(string userData) {
    Contract.Ensures(Contract.Result<string>() != null);
    UserModel deserializedUser = JsonConvert.DeserializeObject<UserModel>(userData);
    // 这里写你的后续业务逻辑
    return "你的返回内容";
}

模型里的ObjectId字段不用改,Json.NET和MongoDB驱动已经兼容了这个类型的序列化,甚至连额外的转换器都不用加。

方案2:把_id改成string类型

如果不想换序列化器,最简单的办法就是把模型里的_id类型改成string,和JSON里的格式对齐:

[DataContract]
public class UserModel {
    [BsonId]
    [BsonRepresentation(BsonType.String)] // 这个特性留着,MongoDB会自动把字符串转成ObjectId存储
    [DataMember]
    public string _id { get; set; }
    // 其他字段保持不变
}

这样DataContractJsonSerializer就能正常解析了,之后如果要操作MongoDB,随时用ObjectId.Parse(_id)把字符串转成ObjectId就行。

方案3:给ObjectId写自定义数据契约转换器(不推荐)

如果你非得用DataContractJsonSerializer还想保留ObjectId类型,那就得写个自定义的IDataContractSurrogate来处理转换,但这个方法比较麻烦,算是下策:

public class ObjectIdSurrogate : IDataContractSurrogate {
    public Type GetDataContractType(Type type) {
        return type == typeof(ObjectId) ? typeof(string) : type;
    }

    public object GetDeserializedObject(object obj, Type targetType) {
        if (targetType == typeof(ObjectId) && obj is string idStr) {
            return ObjectId.Parse(idStr);
        }
        return obj;
    }

    public object GetObjectToSerialize(object obj, Type targetType) {
        return obj is ObjectId oid ? oid.ToString() : obj;
    }

    // 下面这些方法直接返回默认值即可
    public object GetCustomDataToExport(Type clrType, Type dataContractType) => null;
    public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType) => null;
    public void GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection<Type> customDataTypes) {}
    public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData) => null;
    public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit) => typeDeclaration;
}

然后创建序列化器的时候指定这个Surrogate:

var serializerSettings = new DataContractJsonSerializerSettings {
    DataContractSurrogate = new ObjectIdSurrogate()
};
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(UserModel), serializerSettings);
deserializedUser = ser.ReadObject(ms) as UserModel;

额外小提示

  • 先确认POST过来的userData是纯JSON字符串,有时候前端可能会传带额外空格或者编码错误的内容,你可以在控制器里先Console.WriteLine(userData)看看实际内容和你给的示例是不是完全一致。
  • 给UserModel里的registeredAt加个默认值,比如public int registeredAt { get; set; } = 0;,避免以后因为缺这个字段出别的问题。

内容的提问来源于stack exchange,提问作者Michal Takáč

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:06:40