如何让客户端兼容API命名大小写及格式差异?
第三方服务JSON反序列化解决方案
现有客户端应用需向多个可靠性较低、字段名频繁变更的第三方服务发起请求,客户端定义的模型如下:
class Demo { string SomeName { get; set; } double SomeValue { get; set; } bool HasValue { get; set; } }
第三方服务返回的JSON存在多种格式变体:
{ "SomeName": "X", "SomeValue": "5.0", "HasValue": "true" } // TitleCase // 或 { "someName": "X", "someValue": "5.0", "hasValue": "true" } // camelCase // 或 { "some_name": "X", "some_value": "5.0", "has_value": "true" } // snake_case // 或 { "SomeName": "X", "someValue": "5.0", "has_value": "true" } // 混合格式
需解决以下问题:
- 如何在System.Text.Json中实现上述多种JSON格式到同一客户端模型的正确映射?
- 如何确保DeserializeAsync方法在反序列化失败时,始终返回带默认值的模型而非NULL?
注:仅涉及JSON解析/读取/反序列化,无需考虑序列化/写入操作。
问题1:多格式JSON到模型的映射实现
要兼容TitleCase、camelCase、snake_case及混合格式的字段名,推荐使用自定义JsonConverter的方式(System.Text.Json原生不支持单个字段绑定多个别名,自定义转换器可灵活处理所有字段变体):
自定义Demo类转换器
编写JsonConverter<Demo>,在读取JSON时逐个匹配字段的所有可能命名变体:
public class DemoConverter : JsonConverter<Demo> { public override Demo Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.StartObject) { return new Demo(); } var demo = new Demo(); while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndObject) { return demo; } if (reader.TokenType != JsonTokenType.PropertyName) { continue; } string propertyName = reader.GetString(); reader.Read(); // 匹配SomeName的所有命名变体 if (IsMatch(propertyName, "SomeName", "someName", "some_name")) { demo.SomeName = reader.GetString(); } // 匹配SomeValue的所有命名变体 else if (IsMatch(propertyName, "SomeValue", "someValue", "some_value")) { double.TryParse(reader.GetString(), out var value); demo.SomeValue = value; } // 匹配HasValue的所有命名变体 else if (IsMatch(propertyName, "HasValue", "hasValue", "has_value")) { bool.TryParse(reader.GetString(), out var hasValue); demo.HasValue = hasValue; } } return demo; } // 辅助方法:判断字段名是否匹配任一目标名称 private bool IsMatch(string input, params string[] targets) { foreach (var target in targets) { if (string.Equals(input, target, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } // 按要求忽略序列化逻辑 public override void Write(Utf8JsonWriter writer, Demo value, JsonSerializerOptions options) { throw new NotImplementedException(); } }
使用时将转换器加入JsonSerializerOptions:
var options = new JsonSerializerOptions(); options.Converters.Add(new DemoConverter()); // 反序列化调用 var demo = await JsonSerializer.DeserializeAsync<Demo>(responseStream, options);
补充:仅兼容大小写变体的简化方案
若只需处理TitleCase和camelCase的大小写差异,可直接开启属性名称不区分大小写,无需自定义转换器:
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; var demo = await JsonSerializer.DeserializeAsync<Demo>(responseStream, options);
注意:此方案无法识别snake_case格式的下划线字段
问题2:反序列化失败返回默认模型
要确保反序列化过程中无论出现格式错误、流读取异常等情况,都返回带默认值的Demo实例,最直接的方式是捕获所有可能的异常并返回新实例:
public async Task<Demo> SafeDeserializeDemo(Stream stream) { var options = new JsonSerializerOptions(); options.Converters.Add(new DemoConverter()); try { return await JsonSerializer.DeserializeAsync<Demo>(stream, options) ?? new Demo(); } catch (JsonException) { // JSON格式错误 return new Demo(); } catch (IOException) { // 流读取异常 return new Demo(); } catch (ArgumentException) { // 参数非法等其他异常 return new Demo(); } }
即使启用了自定义转换器,若输入的JSON不是合法对象(比如是数组或纯字符串),仍会抛出异常,因此必须通过捕获异常来兜底返回默认模型。
内容的提问来源于stack exchange,提问作者Anonymous
相关产品推荐
相关产品推荐

