https://github.com/furkan3ayraktar/json-schema
A library to generate JSON schemas from Clojure specs.
https://github.com/furkan3ayraktar/json-schema
clojure-spec json-schema
Last synced: 12 days ago
JSON representation
A library to generate JSON schemas from Clojure specs.
- Host: GitHub
- URL: https://github.com/furkan3ayraktar/json-schema
- Owner: furkan3ayraktar
- License: epl-1.0
- Created: 2018-06-17T23:08:51.000Z (almost 8 years ago)
- Default Branch: develop
- Last Pushed: 2018-06-17T23:33:11.000Z (almost 8 years ago)
- Last Synced: 2026-02-25T19:38:54.633Z (about 1 month ago)
- Topics: clojure-spec, json-schema
- Language: Clojure
- Homepage:
- Size: 10.7 KB
- Stars: 7
- Watchers: 1
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# json-schema
A library to generate JSON schemas from Clojure specs.
It provides utility functions to convert both Clojure specs
and [spec-tools](https://github.com/metosin/spec-tools) specs
into [JSON schema](http://json-schema.org).
[](https://clojars.org/com.furkanbayraktar/json-schema)
[](LICENSE)
[](https://circleci.com/gh/furkan3ayraktar/json-schema/tree/master)
## Installation
To include `json-schema`, add the following to your
`:dependencies`:
[com.furkanbayraktar/json-schema "0.1.0"]
## Usage
json-schema provides a function called `spit-schemas!` to write
JSON schemas to individual files. By default, it uses `schemas`
folder in your project root. Here is a sample use case:
```clojure
(ns json-schema-test.core
(:require [clojure.spec.alpha :as s]
[clojure.string :as str]
[com.furkanbayraktar.json-schema.core :as js]
[spec-tools.core :as st]
[spec-tools.data-spec :as ds]))
;; Clojure specs
(s/def :test/field-1 (s/and string? #(not (str/blank? %))))
(s/def :test/field-2 pos-int?)
(s/def :test/field-3 boolean?)
(s/def :test/test-spec (s/keys :req-un [:test/field-1 :test/field-2]
:opt-un [:test/field-3]))
;; spec-tools specs
(def my-integer? (assoc (st/spec integer?)
:description "It's an int"
:name :test/my-integer))
(def not-blank-string-regex #"^.*\S.*$")
(def not-blank-string?
(st/spec {:spec :test/field-1
:type :string
:description "A non blank string checked by clojure.string/blank?"
:json-schema/pattern (str not-blank-string-regex)}))
;; spec-tools data spec
;; spec-tools data spec does not add :name to Spec record, so it needs to
;; be added manually.
(def test-data-spec
(assoc
(ds/spec {:name :test/data-spec
:spec {:id my-integer?
:name :test/field-1
:description not-blank-string?
:birthday (s/int-in 1900 2018)}})
:name :test/data-spec))
;; Function to be called through leiningen (or anywhere else) when needed
(defn generate-schemas! []
(js/spit-schemas! [:test/test-spec my-integer? test-data-spec]))
```
Once you have a function like `generate-schemas!` that calls spit-schemas!,
you can define an alias in your `project.clj` file in order to generate
schema files on your continuous integration pipeline or any time you need
outside of the project. You can learn more about creating aliases on
[leiningen documentation](https://github.com/technomancy/leiningen/blob/master/doc/PLUGINS.md#not-writing-a-plugin-无为).
```clojure
(defproject json-schema-test "0.1.0-SNAPSHOT"
:description "Test project for json-schema"
:dependencies [[com.furkanbayraktar/json-schema "0.1.0"]
[metosin/spec-tools "0.7.0"]
[org.clojure/clojure "1.9.0"]]
;; Alias that calls generate-schemas! function to generate
;; JSON schemas from your specs!
:aliases {"generate-schemas" ["run" "-m" "json-schema-test.core/generate-schemas!"]})
```
The alias named `generate-schemas` will let you run:
$ lein generate-schemas
on your command line. It will generate an output like this:
```
$ lein generate-schemas
JSON Schema: Deleting old schemas.
JSON Schema: Deleting /Users/furkan/test-json-schema/schemas/TestSpec.json
JSON Schema: Deleting /Users/furkan/test-json-schema/schemas/MyInteger.json
JSON Schema: Creating /Users/furkan/test-json-schema/schemas/DataSpec.json
JSON Schema: Creating schema files.
JSON Schema: Creating /Users/furkan/test-json-schema/schemas/TestSpec.json
JSON Schema: Creating /Users/furkan/test-json-schema/schemas/MyInteger.json
JSON Schema: Creating /Users/furkan/test-json-schema/schemas/DataSpec.json
JSON Schema: Done!
```
If you open `DataSpec.json` file under `schemas` folder, you will see:
```json
{
"type" : "object",
"properties" : {
"id" : {
"type" : "integer",
"description" : "It's an int",
"title" : "test/my-integer"
},
"name" : {
"type" : "string"
},
"description" : {
"type" : "string",
"description" : "A non blank string checked by clojure.string/blank?",
"pattern" : "^.*\\S.*$"
},
"birthday" : {
"allOf" : [ {
"type" : "integer",
"format" : "int64"
}, {
"minimum" : 1900,
"maximum" : 2018
} ]
}
},
"required" : [ "id", "name", "description", "birthday" ],
"title" : "test/data-spec"
}
```
You can find different options you can pass to `spit-schemas!` function
in the next section or in the documentation of function.
json-schema contains other utility functions like `spec->json-schema` and
`spec->json-schema-map`. `spec->json-schema` returns a JSON schema string
for given spec. Similarly, `spec->json-schema-map` returns a Clojure map
of the JSON Schema, if you prefer to convert JSON string later on.
```clojure
(spec->json-schema :test/test-spec)
```
evaluates to:
```clojure
=>
"{
\"type\" : \"object\",
\"properties\" : {
\"field-1\" : {
\"type\" : \"string\"
},
\"field-2\" : {
\"type\" : \"integer\",
\"format\" : \"int64\",
\"minimum\" : 1
},
\"field-3\" : {
\"type\" : \"boolean\"
}
},
\"required\" : [ \"field-1\", \"field-2\" ]
}"
```
Similarly,
```clojure
(spec->json-schema-map :test/test-spec)
```
evaluates to:
```clojure
=>
{:type "object",
:properties {"field-1" {:type "string"},
"field-2" {:type "integer", :format "int64", :minimum 1},
"field-3" {:type "boolean"}},
:required ["field-1" "field-2"]}
```
## Options
```clojure
(defn spit-schemas!
([specs {:keys [delete-old-schemas? ; If false, does not delete old schema files before creating new.
output-path ; Defines the output folder for generated schema files.
additional-props? ; If false, adds adds additionalProperties field with false value to schema.
convert-name? ; If false, keeps spec name as file name, otherwise converts it to PascalCase
pretty?] ; If false, skips prettifying JSON string.
:or {delete-old-schemas? true
output-path "schemas"
additional-props? true
convert-name? true
pretty? true}}]
; ...
```
```clojure
(defn spec->json-schema
([spec
additional-props? ; If false, adds additionalProperties field with false value to schema.
pretty?] ; If false, skips prettifying JSON string.
; ...
([spec]
(spec->json-schema spec true true)))
```
```clojure
(defn spec->json-schema-map
([spec
additional-props?] ; If false, adds additionalProperties field with false value to schema.
; ...
([spec]
(spec->json-schema-map spec true)))
```
Reference to [additionalProperties](http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.5.6)
can be found in the link.
Reference to [Pascal Case](https://en.wikipedia.org/wiki/Camel_case) can be found in the link.
## License
Copyright © 2018 Furkan Bayraktar
Distributed under the Eclipse Public License, the same as Clojure.