.NET6中使用Microsoft JsonSerializer实现属性必填校验
在.NET6中用System.Text.Json实现必填属性检测
由于.NET7才引入[JsonRequired]特性,在.NET6环境下要实现反序列化时检测缺失必填属性的功能,可以通过以下几种方式实现:
方法一:自定义JsonConverter验证必填属性
针对需要校验的实体类编写自定义转换器,在反序列化过程中追踪必填属性是否在JSON中存在,不存在则抛出异常。
示例代码
public class innerJModel { public int someProp { get; set; } } public class innerJModelConverter : JsonConverter<innerJModel> { public override innerJModel Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.StartObject) { throw new JsonException("JSON格式错误,预期对象起始标识"); } var model = new innerJModel(); bool somePropFound = false; while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndObject) break; if (reader.TokenType == JsonTokenType.PropertyName) { string propName = reader.GetString(); reader.Read(); if (propName.Equals("someProp", StringComparison.OrdinalIgnoreCase)) { model.someProp = reader.GetInt32(); somePropFound = true; } } } if (!somePropFound) { throw new JsonException("JSON中缺少必填属性: someProp"); } return model; } public override void Write(Utf8JsonWriter writer, innerJModel value, JsonSerializerOptions options) { JsonSerializer.Serialize(writer, value, options); } } public class JModel { public innerJModel innerJModel { get; set; } } public static void test() { JsonSerializerOptions FromJson_JsonSerOpts = new JsonSerializerOptions(JsonSerializerDefaults.General) { AllowTrailingCommas = true, ReadCommentHandling = JsonCommentHandling.Skip, IncludeFields = false, }; FromJson_JsonSerOpts.Converters.Add(new innerJModelConverter()); JsonSerializerOptions ToJson_JsonSerOpts = new JsonSerializerOptions(JsonSerializerDefaults.General) { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, }; string jsonStr = @"{""innerJModel"":{""someProp"":12}}"; var serRes = JsonSerializer.Deserialize<JModel>(jsonStr, FromJson_JsonSerOpts); Console.WriteLine(serRes.innerJModel?.someProp); Console.WriteLine(JsonSerializer.Serialize(serRes, ToJson_JsonSerOpts)); string jsonStr2 = @"{""innerJModel"":{}}"; try { var serRes2 = JsonSerializer.Deserialize<JModel>(jsonStr2, FromJson_JsonSerOpts); Console.WriteLine(JsonSerializer.Serialize(serRes2, ToJson_JsonSerOpts)); } catch (JsonException ex) { Console.WriteLine($"反序列化失败: {ex.Message}"); } }
方法二:反序列化后手动验证模型
在反序列化完成后,对模型的必填属性进行手动校验,不符合要求则抛出异常,适合简单模型场景。
示例代码
public class innerJModel { public int someProp { get; set; } public void Validate() { // 若0是非法输入,可直接判断默认值;若0合法,需结合JsonElement判断属性是否存在 if (someProp == default) { throw new InvalidOperationException("属性someProp为必填项"); } } } public class JModel { public innerJModel innerJModel { get; set; } public void Validate() { if (innerJModel == null) { throw new InvalidOperationException("属性innerJModel为必填项"); } innerJModel.Validate(); } } public static void test() { JsonSerializerOptions FromJson_JsonSerOpts = new JsonSerializerOptions(JsonSerializerDefaults.General) { AllowTrailingCommas = true, ReadCommentHandling = JsonCommentHandling.Skip, IncludeFields = false, }; JsonSerializerOptions ToJson_JsonSerOpts = new JsonSerializerOptions(JsonSerializerDefaults.General) { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, }; string jsonStr = @"{""innerJModel"":{""someProp"":12}}"; var serRes = JsonSerializer.Deserialize<JModel>(jsonStr, FromJson_JsonSerOpts); serRes.Validate(); Console.WriteLine(serRes.innerJModel?.someProp); Console.WriteLine(JsonSerializer.Serialize(serRes, ToJson_JsonSerOpts)); string jsonStr2 = @"{""innerJModel"":{}}"; try { var serRes2 = JsonSerializer.Deserialize<JModel>(jsonStr2, FromJson_JsonSerOpts); serRes2.Validate(); Console.WriteLine(JsonSerializer.Serialize(serRes2, ToJson_JsonSerOpts)); } catch (InvalidOperationException ex) { Console.WriteLine($"验证失败: {ex.Message}"); } }
方法三:结合JsonElement严格校验属性存在性
先将JSON解析为JsonElement,直接检查必填属性是否存在,避免默认值干扰,适合需要严格校验属性存在性的场景。
示例代码
public static void test() { JsonSerializerOptions FromJson_JsonSerOpts = new JsonSerializerOptions(JsonSerializerDefaults.General) { AllowTrailingCommas = true, ReadCommentHandling = JsonCommentHandling.Skip, IncludeFields = false, }; JsonSerializerOptions ToJson_JsonSerOpts = new JsonSerializerOptions(JsonSerializerDefaults.General) { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, }; string jsonStr2 = @"{""innerJModel"":{}}"; try { var jsonElement = JsonSerializer.Deserialize<JsonElement>(jsonStr2, FromJson_JsonSerOpts); if (!jsonElement.TryGetProperty("innerJModel", out var innerElement)) { throw new JsonException("缺少必填属性: innerJModel"); } if (!innerElement.TryGetProperty("someProp", out _)) { throw new JsonException("缺少必填属性: someProp"); } var serRes2 = JsonSerializer.Deserialize<JModel>(jsonStr2, FromJson_JsonSerOpts); Console.WriteLine(JsonSerializer.Serialize(serRes2, ToJson_JsonSerOpts)); } catch (JsonException ex) { Console.WriteLine($"反序列化失败: {ex.Message}"); } }
方案选择建议
- 自定义转换器:适合全局统一处理,侵入性低,无需修改实体类逻辑;
- 手动验证:适合简单模型,实现成本低,逻辑直观;
- JsonElement解析:适合需要严格判断属性是否存在的场景,避免默认值误判。
内容的提问来源于stack exchange,提问作者Andrea Bardelli
相关产品推荐
相关产品推荐

