2016-11-08 4 views
9

ich folgende Json Schema Beispiel zu replizieren bin versucht, durch das Schema in Code definieren Newtonsoft.Json.Schema mit:Wie definiere ich eine Json Schemadefinitionen enthält, in Code

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

Dies ist so nah wie ich habe bisher. (Beispiel ist in F #, sondern könnte genauso gut in C# sein.)

Code:

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 

Ausgang:

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

Wie Sie sehen können, nur eine der beiden Adressen definiert ist Verwenden eines Verweises auf ein anderes Schema, und das Adressschema ist in "Eigenschaften" statt "Definitionen". Was ist der Trick, ein Schema in "Definitionen" zu definieren und es an anderer Stelle zu referenzieren?

Antwort

8

Hackfest! :-)

Nach der source code, JSON.NET-Schema schreibt einfach keine definitions Eigenschaft, Ende der Geschichte. Es ist also alles hoffnungslos ... Fast.

Es tut Verwendung die definitions Eigenschaft an einem anderen Ort, jedoch. Nämlich - when generating schema from a type. Während dieses Prozesses erstellt es eine JObject, schiebt alle Schemas hinein und fügt dieses Objekt JSchema.ExtensionData unter dem Schlüssel definitions hinzu. Und wenn ein Schema von einem anderen Ort aus referenziert wird, respektiert der Schema-Schreiber das definitions Objekt, falls vorhanden, wodurch die ganze Sache zusammenarbeitet.

Also, mit diesem Wissen bewaffnet, können wir uns auf den Weg hinein hacken:

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 

Und voila! Das resultierende Schema hat jetzt ein definitions Objekt, so wie die heiligen Texte sagen uns, es sollte:

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

Ein paar Anmerkungen:

  1. Der definitions Name ist nichts Besonderes aus der JSON.NET der Punkt Sicht. Wenn Sie die Zeile schema.ExtensionData.["definitions"] in etwas anderes ändern, z. B. schema.ExtensionData.["xyz"], wird es immer noch funktionieren, wobei alle Referenzen auf "#/xyz/address" zeigen.
  2. Dieser ganze Mechanismus ist offensichtlich ein Hack Offenbar nicht, according to James Netwon-King. Die Schlüsseleinsicht scheint zu sein, dass die JsonSchemaWriter in der Lage sein wird, beliebige frühere Erwähnungen von Schemas nachzuschlagen und Verweise darauf an anderen Stellen zu verwenden. Dies erlaubt es, Schemas überall hin zu schieben und zu erwarten, dass sie referenziert werden.
  3. Das op_Implicit Anruf dort ist notwendig. JSchema ist kein Subtyp von JToken, also können Sie es nicht einfach so in definitions.["address"] stecken, Sie müssen es zuerst in JToken umwandeln. Glücklicherweise ist dafür ein implicit cast operator definiert. Leider ist es nicht einfach, da scheint etwas Magie los zu sein. Diese happens transparently in C# (weil Sie wissen, es gibt nicht genug Verwirrung, wie es ist), aber in F # müssen Sie es explizit aufrufen.
+0

Vielen Dank! Wir waren nah dran, aber op_Implicit hat uns über die Linie gebracht. Ich habe ein Problem protokolliert: https://github.com/JamesNK/Newtonsoft.Json.Schema/issues/60. Wird als Antwort markieren, wenn ich das getestet habe. (Ich bin mir sicher, dass es in Ordnung ist.; -) – Kit

+0

Verwendet diesen Ansatz in unserem "realen" Code und es funktionierte wie ein Charme. Danke noch einmal! – Kit

+0

Ich bin froh, Ihnen helfen zu können –

Verwandte Themen