如何实现高性能的自定义CosmosSerializer以在序列化/反序列化时修剪字符串值的空白
如何实现高性能的自定义CosmosSerializer以在序列化/反序列化时修剪字符串值的空白
针对你的需求,最高效的方案是利用Newtonsoft.Json的自定义JsonConverter来处理字符串的空白修剪,而不是用反射遍历属性。这种方式是序列化框架原生支持的扩展点,性能远优于手动反射,而且能自动覆盖所有嵌套的字符串属性,实现全局的空白修剪。
核心思路
通过创建一个专门处理字符串类型的JsonConverter,在序列化和反序列化的过程中自动修剪字符串前后的空白,然后将这个转换器注册到Newtonsoft的JsonSerializer实例中,让Cosmos的序列化流程自动应用这个逻辑。
第一步:实现字符串修剪的JsonConverter
这个转换器只负责处理字符串类型的序列化/反序列化,逻辑非常聚焦:
public class StringTrimmingConverter : JsonConverter<string> { public override string ReadJson(JsonReader reader, Type objectType, string existingValue, bool hasExistingValue, JsonSerializer serializer) { // 读取原始字符串并修剪前后空白,避免空引用异常 var rawValue = reader.Value as string; return rawValue?.Trim(); } public override void WriteJson(JsonWriter writer, string value, JsonSerializer serializer) { // 写入前先修剪空白,同样处理null情况 writer.WriteValue(value?.Trim()); } }
第二步:修改CustomCosmosSerializer,注册转换器
把上面的转换器添加到JsonSerializer的转换器集合中,这样所有字符串类型的属性都会自动应用修剪逻辑:
public class CustomCosmosSerializer : CosmosSerializer { private readonly Newtonsoft.Json.JsonSerializer _serializer; public CustomCosmosSerializer() { _serializer = new Newtonsoft.Json.JsonSerializer(); // 注册自定义字符串修剪转换器,全局生效 _serializer.Converters.Add(new StringTrimmingConverter()); } public override T FromStream<T>(Stream stream) { using var streamReader = new StreamReader(stream); using var jsonTextReader = new JsonTextReader(streamReader); return _serializer.Deserialize<T>(jsonTextReader); } public override Stream ToStream<T>(T input) { var memoryStream = new MemoryStream(); // 保持流打开,避免提前释放导致Cosmos读取失败 using var streamWriter = new StreamWriter(memoryStream, Encoding.UTF8, leaveOpen: true); using var jsonTextWriter = new JsonTextWriter(streamWriter); _serializer.Serialize(jsonTextWriter, input); // 重置流位置到开头,确保Cosmos客户端能正确读取内容 memoryStream.Position = 0; return memoryStream; } }
方案优势
- 性能更优:JsonConverter是Newtonsoft.Json的原生扩展,序列化引擎会直接调用转换器处理字符串类型,完全避免了反射遍历属性的开销,在处理大量文档时性能差异会非常明显。
- 全局覆盖:不管是根对象的字符串属性,还是嵌套对象、数组中的字符串,都会自动应用修剪逻辑,不需要手动处理每个层级。
- 代码简洁易维护:转换器只关注字符串修剪逻辑,CosmosSerializer只负责整合序列化流程,职责清晰,后续修改也很方便。
额外细节优化
- 修正了你原代码中
RequestTimeout的小问题:new TimeSpan(10)表示的是10个时间刻度(约1微秒),这显然不是你想要的,应该改为TimeSpan.FromSeconds(10)来设置10秒超时。 - 如果后续需要对某些特定字符串跳过修剪,可以给对应的属性加上
[JsonConverter(typeof(StringConverter))](使用默认的字符串转换器),覆盖全局的修剪逻辑。
最终CosmosClient初始化代码
var cosmosClientOptions = new CosmosClientOptions { ConnectionMode = ConnectionMode.Direct, RequestTimeout = TimeSpan.FromSeconds(10), Serializer = new CustomCosmosSerializer() }; private readonly CosmosClient _cosmosClient = new CosmosClient(uri, key, cosmosClientOptions);
备注:内容来源于stack exchange,提问作者ben
相关产品推荐
相关产品推荐

