反序列化带$id的Cosmos DB对象时出现JsonSerializationException如何解决?
错误根因
你配置的MetadataPropertyHandling.Ignore会让Newtonsoft.Json将$id、$ref这类JSON元数据字段识别为普通业务字段,而非引用处理标记。当PreserveReferencesHandling.All序列化集合类型时,会生成{"$id":"xxx","$values":[实际集合内容]}的结构,反序列化时元数据处理被关闭,Json.NET会将该结构识别为普通对象,自然无法转换为List<T>/数组类型,触发类型不匹配错误。
解决方案
方案1:保留引用处理能力(无需修改存量数据)
如果你的业务需要用到PreserveReferencesHandling提供的循环引用处理、引用复用能力,只需要将MetadataPropertyHandling从Ignore修改为Default即可,Json.NET会自动识别$开头的元数据字段,正确解析带$id的集合:
var cosmosClientOptions = new CosmosClientOptions() { ConnectionMode = ConnectionMode.Direct, Serializer = new CosmosJsonNetSerializer(new JsonSerializerSettings() { MetadataPropertyHandling = MetadataPropertyHandling.Default, // 仅修改这一行 ReferenceLoopHandling = ReferenceLoopHandling.Ignore, PreserveReferencesHandling = PreserveReferencesHandling.All, NullValueHandling = NullValueHandling.Include, TypeNameHandling = TypeNameHandling.None, }) };
该方案修改成本极低,不需要调整存量数据,新写入的数据仍会保留$id字段实现引用处理逻辑。
方案2:彻底移除$id字段(兼容存量数据+新数据无冗余字段)
如果你不需要引用处理能力,希望存量和新数据都不带额外的$id元数据字段,按以下步骤配置:
- 新增自定义Json转换器,处理存量数据中带$id的集合解析:
public class IgnoreReferenceMetadataConverter : JsonConverter { public override bool CanConvert(Type objectType) { // 适配所有集合、数组类型 return objectType.IsArray || (objectType.IsGenericType && typeof(IEnumerable).IsAssignableFrom(objectType) && objectType != typeof(string)); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JToken token = JToken.Load(reader); // 处理带$id元数据的集合结构 if (token.Type == JTokenType.Object && ((JObject)token).TryGetValue("$values", out JToken collectionValue)) { return collectionValue.ToObject(objectType, serializer); } return token.ToObject(objectType, serializer); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { serializer.Serialize(writer, value); } }
- 调整序列化配置,关闭引用处理并注册转换器:
var cosmosClientOptions = new CosmosClientOptions() { ConnectionMode = ConnectionMode.Direct, Serializer = new CosmosJsonNetSerializer(new JsonSerializerSettings() { MetadataPropertyHandling = MetadataPropertyHandling.Ignore, ReferenceLoopHandling = ReferenceLoopHandling.Ignore, PreserveReferencesHandling = PreserveReferencesHandling.None, // 关闭引用处理 NullValueHandling = NullValueHandling.Include, TypeNameHandling = TypeNameHandling.None, Converters = new List<JsonConverter> { new IgnoreReferenceMetadataConverter() } // 注册转换器 }) };
该方案可直接兼容存量带$id的文档,新写入的文档不会再生成冗余的元数据字段。
内容的提问来源于stack exchange,提问作者SauravBhattacharya
相关产品推荐
相关产品推荐

