.NET OpenSearch客户端序列化器如何实现自定义JSON转换?
问题背景
我的OpenSearch中存在不一致的数据:某个属性的值有时是单个字符串,有时是字符串列表,且无法控制这种数据格式。在使用System.Text.Json序列化器时,我可以通过自定义JSON转换器将字符串列表自动转为逗号分隔的字符串,只需在目标属性上添加特性即可。现在需要在.NET的OpenSearch客户端中实现相同功能。
解决方案
.NET OpenSearch客户端(OpenSearch.Client)支持配置使用System.Text.Json作为序列化器,你可以复用现有的自定义转换器,只需在客户端初始化时配置对应的序列化选项,或直接在属性上保留原有的特性标记。
步骤1:保留原有的自定义转换器
继续使用你已实现的ListOfStringToStringConverter,同时修正一处JSON规范问题:
using Serilog; using System; using System.Text.Json; using System.Text.Json.Serialization; namespace myProject { public class ListOfStringToStringConverter : JsonConverter<string> { public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.Null) return null; string strValue = ""; try { if (JsonTokenType.String == reader.TokenType) { strValue = reader.GetString(); return strValue; } else if (JsonTokenType.StartArray == reader.TokenType) { reader.Read(); string val = ""; int i = 0; while (reader.TokenType != JsonTokenType.EndArray) { if (i != 0) val += ","; val += reader.GetString(); i++; reader.Read(); } return val; } } catch (Exception e) { Log.Error(e, "反序列化JSON值到属性时出错,属性值将设为null"); } return null; } public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) { if (value == null) { writer.WriteNullValue(); } else { writer.WriteStringValue(value); } } } }
注:修正了原代码中把null序列化为字符串"null"的问题,改为符合JSON规范的WriteNullValue()。
步骤2:在实体类属性上保留特性标记
继续在目标属性上添加JsonConverter特性,指定使用自定义转换器:
[System.Text.Json.Serialization.JsonConverter(typeof(ListOfStringToStringConverter))] public string? ItUsedToBeAList { get; set; }
步骤3:配置OpenSearch客户端使用System.Text.Json序列化器
初始化OpenSearchClient时,通过OpenSearchClientSettings指定使用System.Text.Json作为序列化器,并传入包含自定义转换器的JsonSerializerOptions:
using OpenSearch.Client; using System.Text.Json; // 创建JsonSerializerOptions并添加自定义转换器 var jsonOptions = new JsonSerializerOptions(); jsonOptions.Converters.Add(new ListOfStringToStringConverter()); // 配置OpenSearch客户端连接与序列化设置 var settings = new OpenSearchClientSettings(new Uri("http://your-opensearch-url:9200")) .UseSystemTextJson(jsonOptions); // 初始化OpenSearch客户端实例 var client = new OpenSearchClient(settings);
替代方案:全局注册转换器(无需属性特性)
如果希望该转换器对所有字符串类型属性生效,可以跳过属性上的特性标记,直接将转换器添加到全局的JsonSerializerOptions中,客户端在序列化/反序列化所有字符串属性时都会自动应用该逻辑。
验证逻辑
当OpenSearch返回的ItUsedToBeAList字段是单个字符串时,会直接赋值给属性;如果是字符串数组,转换器会自动拼接为逗号分隔的字符串;遇到空值或解析错误时,属性会被设为null并记录错误日志。
内容的提问来源于stack exchange,提问作者LilacBlue

