2016-11-08 31 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

很高興能有所幫助 –

相關問題