我试图通过使用Json Schema
在代码中定义架构来复制以下Newtonsoft.Json.Schema
示例:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"address": {
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"]
}
},
"type": "object",
"properties": {
"billing_address": { "$ref": "#/definitions/address" },
"shipping_address": { "$ref": "#/definitions/address" }
}
这与我到目前为止一样接近。 (示例在F#中,但也可能在C#中。)
代码:
open Newtonsoft.Json.Schema
open Newtonsoft.Json.Linq
let makeSchema =
let addressSchema = JSchema()
addressSchema.Properties.Add("street_address", JSchema(Type = Nullable(JSchemaType.String)))
addressSchema.Properties.Add("city", JSchema(Type = Nullable(JSchemaType.String)))
addressSchema.Properties.Add("state", JSchema(Type = Nullable(JSchemaType.String)))
addressSchema.Required.Add "street_address"
addressSchema.Required.Add "city"
addressSchema.Required.Add "state"
let schema = JSchema()
schema.Properties.Add("billing_address", addressSchema)
schema.Properties.Add("shipping_address", addressSchema)
schema
输出:
{
"properties": {
"billing_address": {
"properties": {
"street_address": {
"type": "string"
},
"city": {
"type": "string"
},
"state": {
"type": "string"
}
},
"required": [
"street_address",
"city",
"state"
]
},
"shipping_address": {
"$ref": "#/properties/billing_address"
}
}
}
正如您所看到的,两个地址中只有一个是使用对另一个模式的引用定义的,而地址模式位于"属性"而不是"定义"。在"定义"中定义模式的诀窍是什么?并在其他地方引用它?
答案 0 :(得分:9)
Hackfest! : - )
根据source code,JSON.NET Schema只是没有写一个definitions
属性,故事结束。所以这一切都无望......差不多。
然而,在其他地方使用 definitions
属性。即 - when generating schema from a type。在此过程中,它会创建一个JObject
,将所有架构推入其中,然后将该对象添加到JSchema.ExtensionData
键下的definitions
。当从其他地方引用模式时,模式编写器将尊重definitions
对象(如果存在),从而使整个事物协同工作。
因此,凭借这些知识,我们可以破解它:
let makeSchema =
let addressSchema = JSchema()
...
let definitions = JObject() :> JToken
definitions.["address"] <- addressSchema |> JSchema.op_Implicit
let schema = JSchema()
schema.ExtensionData.["definitions"] <- definitions
schema.Properties.Add("billing_address", addressSchema)
schema.Properties.Add("shipping_address", addressSchema)
schema
瞧!结果模式现在有一个definitions
对象,就像神圣的文本告诉我们它应该:
{
"definitions": {
"address": {
"properties": {
"street_address": {
"type": "string"
},
"city": {
"type": "string"
},
"state": {
"type": "string"
}
},
"required": [
"street_address",
"city",
"state"
]
}
},
"properties": {
"billing_address": {
"$ref": "#/definitions/address"
},
"shipping_address": {
"$ref": "#/definitions/address"
}
}
}
一些注释:
definitions
名称并不特别。如果您将行schema.ExtensionData.["definitions"]
更改为其他内容,请说schema.ExtensionData.["xyz"]
,它仍然有效,所有引用都指向"#/xyz/address"
。JsonSchemaWriter
将能够查找以前提到的任何模式并在其他地方使用对它们的引用。这允许人们在任何人喜欢的地方推送模式并期望它们被引用。op_Implicit
电话是必要的。 JSchema
不是JToken
的子类型,因此您无法将其加入definitions.["address"]
,您必须先将其转换为JToken
。幸运的是,为此定义了implicit cast operator。不幸的是,它并不简单,似乎有一些魔力在继续。这个happens transparently in C#(因为,你知道,它没有足够的混淆),但在F#你必须明确地调用它。