问题描述
有没有什么方法可以使用 Newtonsoft.Json 强制解析 F# 中记录类型的非空字符串字段?
#r """Newtonsoft.Json.dll"""
open Newtonsoft.Json
type Customer = {
Name: string
Email: string
ContactPhoneNo: string
}
// one or more fields can be empty
let customer = {
Name = ""
Email = "ca@gmail.com"
ContactPhoneNo = "+123456789"
}
let serializedCustomer =
JsonConvert.SerializeObject(customer)
// this parses correctly with the Name field set as ""
// But as the name field is empty,it should not parse it
let deserializedCustomer =
JsonConvert.DeserializeObject<Customer>(serializedCustomer)
解决方法
您可能需要考虑为此使用 Newtonsoft 的架构支持,它位于一个名为 Newtonsoft.Json.Schema
的单独包中。您可以使用注释指定许多不同类型的约束。例如,要禁止空白名称,您可以使用 MinLength
:
open System.ComponentModel.DataAnnotations
type Customer = {
[<MinLength(1)>]
Name: string
Email: string
ContactPhoneNo: string
}
在对类型进行注释后,您可以生成架构:
let generator = JSchemaGenerator()
let schema = generator.Generate(typeof<Customer>)
然后用它来验证序列化的 JSON:
let jsonCustomer = JObject.Parse(serializedCustomer)
let isValid = jsonCustomer.IsValid(schema)
如果您想跳过首先将 JSON 加载到 JObject
以对其进行验证的开销,您可以改用 JSchemaValidatingReader
:
use strReader = new System.IO.StringReader(serializedCustomer)
use txtReader = new JsonTextReader(strReader)
use vldReader = new JSchemaValidatingReader(txtReader,Schema = schema)
let messages = ResizeArray()
vldReader.ValidationEventHandler.Add(fun args -> messages.Add(args.Message))
let serializer = JsonSerializer()
let deserializedCustomer = serializer.Deserialize<Customer>(vldReader)
printfn "%A" deserializedCustomer
let isValid = (messages.Count = 0)
printfn "%A" isValid
有关详细信息,请参阅 this documentation。
,您可以实现一个自定义 JsonConverter
来转换 string
类型的值,但在字符串为空时抛出异常:
let nonEmptyStringConverter =
{ new JsonConverter() with
override x.CanConvert(objectType) = objectType = typeof<string>
override x.WriteJson(writer,value,serializer) =
JValue(value :?> string).WriteTo(writer)
override x.ReadJson(reader,objectType,existingValue,serializer) =
let jt = JToken.Load(reader)
if jt.Type = JTokenType.String then
let str = jt.Value<string>()
if String.IsNullOrEmpty str then failwith "Empty string"
box str
else failwith "Expected a string" }
如果您将其传递给 DeserializeObject
,那么它将在您的示例中引发异常:
let serializedCustomer =
JsonConvert.SerializeObject(customer)
let deserializedCustomer =
JsonConvert.DeserializeObject<Customer>(serializedCustomer,nonEmptyStringConverter)
一个警告是,这将适用于类型中的所有 string
值,我希望您可能希望允许某些值为空。更好的方法是定义自定义类型并仅为此类型定义转换器,例如使用:
type NonEmptyString = NE of string
type Customer =
{ Name : NonEmptyString
Email: NonEmptyString
ContactPhoneNo: string }