2016-11-08 80 views
9

我试图复制以下Json Schema例如,通过使用Newtonsoft.Json.Schema定义代码架构:如何定义一个JSON模式包含了定义,在代码

{ 
    "$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" 
    } 
    } 
} 

正如你所看到的,只有两个地址的一个定义使用对另一个模式的引用,并且地址模式在“属性”而不是“定义”中。在“定义”中定义模式并在其他地方引用它有什么窍门?

回答

8

Hackfest! :-)

根据source code,JSON.NET架构只写了definitions属性,故事结束。所以这都是无望的......差不多。

它确实使用definitions财产在另一个地方,但是。即 - when generating schema from a type。在此过程中,它创建一个JObject,将所有模式推入其中,然后将该对象添加到JSchema.ExtensionDatadefinitions密钥下。并且,当从另一个地方引用架构时,架构编写者将会尊重该对象(如果存在),从而使整个事物一起工作。

所以,有了这些知识,我们可以入侵我们的方式把它:

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" 
    } 
    } 
} 

的几个注意事项:

  1. definitions名字是不是从JSON.NET的特殊点看法。如果您将行schema.ExtensionData.["definitions"]更改为不同的内容,例如schema.ExtensionData.["xyz"],它仍然可以工作,所有参考都指向"#/xyz/address"
  2. 这整个机制,显然是一个破解 显然不是,according to James Netwon-King。关键的见解似乎是JsonSchemaWriter将能够查找任何以前提及的模式,并在其他地方使用对它们的引用。这允许人们在任何喜欢的地方推广模式,并期望他们被引用。
  3. op_Implicit有必要。 JSchema不是JToken的子类型,所以你不能像这样将其卡入到definitions.["address"]中,必须先将其转换为JToken。幸运的是,为此定义了一个implicit cast operator。不幸的是,这不是直截了当的,似乎有一些魔法正在发生。这个happens transparently in C#(因为,你知道,它没有足够的混淆),但是在F#中你必须明确地调用它。
+0

谢谢这么多!我们已经接近这一点,但op_Implicit是推动我们超越界限的原因。我已经记录了一个问题:https://github.com/JamesNK/Newtonsoft.Json.Schema/issues/60。当我测试这个时,将会标记为答案。 (我确定它没问题; - ) – Kit

+0

在我们的“真实世界”代码中使用了这种方法,它的工作就像一个魅力。再次感谢! – Kit

+0

很高兴能有所帮助 –

相关问题