Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## Unreleased

- Make the validator configurable via an options map. You can use it to enable [inline dereferencing][inline-deref] by passing `{:dereferencing :inline}` as the second parameter to `validator`/`json-validator`.

[inline-deref]: (http://json-schema.org/latest/json-schema-core.html#anchor30).

## 0.3.0 (7.8.2016)

- `validator` and `json-validator` now take a `JsonSchemaFactory` as an optional argument. This allows e.g. pre-loading schema definitions.
Expand Down
2 changes: 1 addition & 1 deletion project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject metosin/scjsv "0.3.0"
(defproject metosin/scjsv "0.4.0-SNAPSHOT"
:description "Simple JSON-Schema validator for Clojure"
:url "https://github.com/metosin/scjsv"
:license {:name "Eclipse Public License"
Expand Down
59 changes: 46 additions & 13 deletions src/scjsv/core.clj
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
(ns scjsv.core
(:require [cheshire.core :as c])
(:import [com.fasterxml.jackson.databind ObjectMapper JsonNode]
[com.github.fge.jsonschema.main JsonSchemaFactory]
(:import [com.fasterxml.jackson.databind JsonNode]
[com.github.fge.jackson JsonLoader]
[com.github.fge.jsonschema.main JsonSchemaFactory]
[com.github.fge.jsonschema.core.load Dereferencing]
[com.github.fge.jsonschema.core.load.configuration LoadingConfiguration]
[com.github.fge.jsonschema.core.report ListProcessingReport ProcessingMessage]
[com.github.fge.jsonschema.main JsonSchema]))

(defn- build-factory
"Creates a JsonSchemaFactory based on the options map."
[{:keys [dereferencing] :or {dereferencing :canonical}}]
(let [dereferencing-mode (case dereferencing
:inline (Dereferencing/INLINE)
:canonical (Dereferencing/CANONICAL))
loading-config (-> (LoadingConfiguration/newBuilder)
(.dereferencing dereferencing-mode)
(.freeze))]
(-> (JsonSchemaFactory/newBuilder)
(.setLoadingConfiguration loading-config)
(.freeze))))

(defn- ->json-schema
"Creates a JSONSchema instance either from a JSON string or a Clojure Map."
[schema factory]
[schema ^JsonSchemaFactory factory]
(let [schema-string (if (string? schema)
schema
(c/generate-string schema))
mapper (ObjectMapper.)
schema-object (.readTree ^ObjectMapper mapper ^String schema-string)
json-schema (.getJsonSchema ^JsonSchemaFactory factory ^JsonNode schema-object)]
json-schema))
schema-object (JsonLoader/fromString schema-string)]
(.getJsonSchema factory schema-object)))

(defn- validate
"Validates (f data) against a given JSON Schema."
Expand All @@ -28,23 +41,43 @@
(if (seq errors)
(map ->clj errors))))

(defn- ->factory
"Converts value to a JsonSchemaFactory if it isn't one."
[value]
(cond
(instance? JsonSchemaFactory value) value
(map? value) (build-factory value)
:else (throw (Exception. (str "Don't know how to convert " (pr-str value)
" into a JsonSchemaFactory.")))))

;;
;; Public API
;;

(defn json-validator
"Returns a JSON string validator (a single arity fn).
Schema can be given either as a JSON String or a Clojure Map."
Schema can be given either as a JSON String or a Clojure Map.

To configure the validator, you can pass a JsonSchemaFactory instance or a
options map as the second parameter. See scjsv.core/validator docstring for
the options."
([schema]
(json-validator schema (JsonSchemaFactory/byDefault)))
(json-validator schema (build-factory {})))
([schema json-schema-factory]
(partial validate (->json-schema schema json-schema-factory))))
(partial validate (->json-schema schema (->factory json-schema-factory)))))

(defn validator
"Returns a Clojure data structure validator (a single arity fn).
Schema can be given either as a JSON String or a Clojure Map."
Schema can be given either as a JSON String or a Clojure Map.

To configure the validator, you can pass a JsonSchemaFactory instance or an
options map as the second parameter. The options map can have the following
keys:

:dereferencing -- Which dereferencing mode to use. Either :canonical (default)
or :inline."
([schema]
(validator schema (JsonSchemaFactory/byDefault)))
(validator schema (build-factory {})))
([schema json-schema-factory]
(comp (partial validate (->json-schema schema json-schema-factory))
(comp (partial validate (->json-schema schema (->factory json-schema-factory)))
c/generate-string)))
10 changes: 9 additions & 1 deletion test/scjsv/core_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
(validate-with-explicit-factory valid) => nil
(validate-with-explicit-factory invalid) =not=> nil

(fact "validation errors are lovey clojure maps"
(fact "validation errors are lovely clojure maps"
(validate invalid)
=> [{:domain "validation"
:instance {:pointer "/shipping_address"}
Expand All @@ -49,3 +49,11 @@
:required ["city" "state" "street_address"]
:schema {:loadingURI "#"
:pointer "/definitions/address"}}])))

(fact "Validating a schema that refers to itself"
(let [schema (slurp (io/resource "scjsv/with_id.json"))
default-validate (v/validator schema)
inline-validate (v/validator schema {:dereferencing :inline})
valid {:foo "foo"}]
(default-validate valid) =not=> nil
(inline-validate valid) => nil))
14 changes: 14 additions & 0 deletions test/scjsv/with_id.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "resource:/does/not/exist",
"properties": {
"foo": {
"$ref": "#/definitions/foo"
}
},
"definitions": {
"foo": {
"enum": ["foo"]
}
}
}