C# .NET Core反序列化POST 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áč

