我正在尝试通过使用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"
}
}
}
如您所见,两个地址中只有一个是通过使用对另一个模式的引用来定义的,并且该地址模式位于“属性”而不是“定义”中。在“定义”中定义架构并在其他地方引用它的诀窍是什么?
最佳答案
哈克节! :-)
根据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#中,您必须显式调用它。 关于json - 如何在代码中定义包含定义的Json Schema,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40483028/