11# JSON schema to Elm
22
3- Generates Elm types, JSON decoders and JSON encoders from JSON schema
4- specifications.
3+ Generates Elm types, JSON decoders, JSON encoders, and Fuzz tests from JSON
4+ schema specifications.
55
66## Installation
77
@@ -26,14 +26,14 @@ Run `./js2e` for usage instructions.
2626> have to pass it the enclosing directory of the relevant JSON schema files,
2727> in order for it to be able to resolve the references correctly.
2828
29- A proper description of which properties are mandatory are how the generator
29+ A proper description of which properties are mandatory and how the generator
3030works is still in progress, but feel free to take a look at the ` examples `
3131folder which contains an example of a pair of JSON schemas and their
3232corresponding Elm output. Likewise, representations of each of the different
3333JSON schema types are described in the ` lib/types ` folder.
3434
35- The tool aims to produce ` elm-make ` -like errors if something is missing,
36- mispelled or cannot be resolved in the supplied JSON schema file(s), e.g.
35+ The tool aims to produce ` elm-make ` -like errors in case something is missing,
36+ misspelled or cannot be resolved in the supplied JSON schema file(s), e.g.
3737
3838```
3939--- UNKNOWN NODE TYPE -------------------------------------- all_of_example.json
@@ -137,21 +137,25 @@ type alias Point =
137137
138138colorDecoder : String -> Decoder Color
139139colorDecoder color =
140- case color of
141- " red" ->
142- succeed Red
140+ Decode . string
141+ |> andThen
142+ ( \ color ->
143+ case color of
144+ " red" ->
145+ succeed Red
143146
144- " yellow" ->
145- succeed Yellow
147+ " yellow" ->
148+ succeed Yellow
146149
147- " green" ->
148- succeed Green
150+ " green" ->
151+ succeed Green
149152
150- " blue" ->
151- succeed Blue
153+ " blue" ->
154+ succeed Blue
152155
153- _ ->
154- fail <| " Unknown color type: " ++ color
156+ _ ->
157+ fail <| " Unknown color type: " ++ color
158+ )
155159
156160
157161pointDecoder : Decoder Point
@@ -253,34 +257,34 @@ import Json.Encode as Encode
253257 , object
254258 , list
255259 )
256- import Data.Definitions
260+ import Data.Definitions as Definitions
257261
258262
259263type alias Circle =
260- { center : Data . Definitions . Point
261- , color : Maybe Data . Definitions . Color
264+ { center : Definitions . Point
265+ , color : Maybe Definitions . Color
262266 , radius : Float
263267 }
264268
265269
266270circleDecoder : Decoder Circle
267271circleDecoder =
268272 decode Circle
269- |> required " center" Data . Definitions . pointDecoder
270- |> optional " color" ( Decode . string |> andThen Data . Definitions . colorDecoder |> maybe ) Nothing
273+ |> required " center" Definitions . pointDecoder
274+ |> optional " color" ( nullable Definitions . colorDecoder) Nothing
271275 |> required " radius" Decode . float
272276
273277
274278encodeCircle : Circle -> Value
275279encodeCircle circle =
276280 let
277281 center =
278- [ ( " center" , Data . Definitions . encodePoint circle. center ) ]
282+ [ ( " center" , Definitions . encodePoint circle. center ) ]
279283
280284 color =
281285 case circle. color of
282286 Just color ->
283- [ ( " color" , Data . Definitions . encodeColor color ) ]
287+ [ ( " color" , Definitions . encodeColor color ) ]
284288
285289 Nothing ->
286290 []
@@ -294,6 +298,170 @@ encodeCircle circle =
294298 ++ radius
295299```
296300
301+ Furthermore, ` js2e ` also generates test files for the generated decoders and
302+ encoders, which fuzzes instances of a given Elm type and tests that encoding it
303+ as JSON and decoding it back into Elm returns the original instance of that
304+ generated Elm type. In the above case, the following test files,
305+ ` tests/Data/CircleTests.elm ` and ` tests/Data/DefinitionsTests.elm ` , are
306+ generated:
307+
308+ ``` elm
309+ module Data.CircleTests exposing (..)
310+
311+ -- Tests: Schema for a circle shape
312+
313+ import Expect exposing (Expectation )
314+ import Fuzz exposing (Fuzzer )
315+ import Test exposing (..)
316+ import Json.Decode as Decode
317+ import Data.Circle exposing (..)
318+ import Data.DefinitionsTests as Definitions
319+
320+
321+ circleFuzzer : Fuzzer Circle
322+ circleFuzzer =
323+ Fuzz . map3
324+ Circle
325+ Definitions . pointFuzzer
326+ ( Fuzz . maybe Definitions . colorFuzzer)
327+ Fuzz . float
328+
329+
330+ encodeDecodeCircleTest : Test
331+ encodeDecodeCircleTest =
332+ fuzz circleFuzzer " can encode and decode Circle object" <|
333+ \ circle ->
334+ circle
335+ |> encodeCircle
336+ |> Decode . decodeValue circleDecoder
337+ |> Expect . equal ( Ok circle)
338+ ```
339+ and
340+
341+ ``` elm
342+ module Data.DefinitionsTests exposing (..)
343+
344+ -- Tests: Schema for common types
345+
346+ import Expect exposing (Expectation )
347+ import Fuzz exposing (Fuzzer )
348+ import Test exposing (..)
349+ import Json.Decode as Decode
350+ import Data.Definitions exposing (..)
351+
352+
353+ colorFuzzer : Fuzzer Color
354+ colorFuzzer =
355+ Fuzz . oneOf
356+ [ Fuzz . constant Red
357+ , Fuzz . constant Yellow
358+ , Fuzz . constant Green
359+ , Fuzz . constant Blue
360+ ]
361+
362+
363+ encodeDecodeColorTest : Test
364+ encodeDecodeColorTest =
365+ fuzz colorFuzzer " can encode and decode Color object" <|
366+ \ color ->
367+ color
368+ |> encodeColor
369+ |> Decode . decodeValue colorDecoder
370+ |> Expect . equal ( Ok color)
371+
372+
373+ pointFuzzer : Fuzzer Point
374+ pointFuzzer =
375+ Fuzz . map2
376+ Point
377+ Fuzz . float
378+ Fuzz . float
379+
380+
381+ encodeDecodePointTest : Test
382+ encodeDecodePointTest =
383+ fuzz pointFuzzer " can encode and decode Point object" <|
384+ \ point ->
385+ point
386+ |> encodePoint
387+ |> Decode . decodeValue pointDecoder
388+ |> Expect . equal ( Ok point)
389+
390+ ```
391+
392+ Finally, ` js2e ` also generates package config files, ` package.json ` and
393+ ` elm-package.json ` making it easy to test that the generated Elm code is
394+ behaving as expected. Thus, if we supply the following directory structure to
395+ ` js2e ` in the above case:
396+
397+ ```
398+ .
399+ └── js2e_input/
400+ ├── definitions.json
401+ └── circle.json
402+ ```
403+
404+ the following new directory structure is generated:
405+
406+ ```
407+ .
408+ └── js2e_output/
409+ ├── package.json
410+ ├── elm-package.json
411+ ├── Data/
412+ │ ├── Circle.elm
413+ │ └── Definitions.elm
414+ └── tests/
415+ ├── elm-package.json
416+ └── Data/
417+ ├── CircleTests.elm
418+ └── DefinitionsTests.elm
419+ ```
420+
421+ containing the files described above along with the needed package config files
422+ to compile and run the tests.
423+
424+ ## Error reporting
425+
426+ Any errors encountered by the ` js2e ` tool while parsing the JSON schema files or
427+ printing the Elm code output, is reported in an Elm-like style, e.g.
428+
429+ ```
430+ --- UNKNOWN NODE TYPE -------------------------------------- all_of_example.json
431+
432+ The value of "type" at '#/allOf/0/properties/description' did not match a known node type
433+
434+ "type": "strink"
435+ ^^^^^^^^
436+
437+ Was expecting one of the following types
438+
439+ ["null", "boolean", "object", "array", "number", "integer", "string"]
440+
441+ Hint: See the specification section 6.25. "Validation keywords - type"
442+ <http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.25>
443+ ```
444+
445+ or
446+
447+ ```
448+ --- UNRESOLVED REFERENCE ----------------------------------- all_of_example.json
449+
450+
451+ The following reference at `#/allOf/0/color` could not be resolved
452+
453+ "$ref": #/definitions/kolor
454+ ^^^^^^^^^^^^^^^^^^^
455+
456+
457+ Hint: See the specification section 9. "Base URI and dereferencing"
458+ <http://json-schema.org/latest/json-schema-core.html#rfc.section.9>
459+ ```
460+
461+ If you encounter an error while using ` js2e ` that does not mimic the above
462+ Elm-like style, but instead looks like an Elixir stacktrace, please report this
463+ as a bug by opening an issue and includin a JSON schema example that recreates
464+ the error.
297465
298466## Contributing
299467
0 commit comments