Description
Elm code generate for json-spec
.
Description
Produce elm types, encoders, and decoders from a `json-spec`Specification
.
See `testtest.hs` for an example.
README.md
json-spec-elm
Produce elm types, encoders, and decoders from a json-spec
Specification
.
See /test/test.hs
for an example.
Example
First let's define an example spec.
A couple of things to note:
- Only things that are named using
JsonLet
will get elm types. (Also note,Named
is just an alias forJsonLet
.) So you will probably want to name the top-level of your spec at least. JsonEither
spec types must be named. Elm can support anonymous record types but not anonymous sum types, so there is no way to embed an anonymousJsonEither
. You have to give it a name.- Naming the constructors for sum types is a little tricky. If a branch of
JsonEither
is given a name (usingNamed
), then we interpret the name as the name of the data constructor, not the name of the type contained within the branch. To name both the data constructor and the type, you must use nestedNamed
s.
type ExampleSpec =
Named "ExampleType"
( JsonObject
'[ '("stringField", JsonString)
, '( "anonymousObject"
, JsonObject
'[ '("floatField", JsonNum)
, '("dateField", JsonDateTime)
, '( "sumType1"
, Named "SumTypeWithCustomConstructorNames"
( JsonEither
( JsonEither
(Named "IntConstructor" JsonInt)
(Named "StringConstructor" JsonString)
)
(Named "FloatConstructor" JsonNum)
)
)
, '( "sumType2"
, Named "SumTypeWithAutomaticConstructorNames"
( JsonEither
( JsonEither
JsonInt
JsonString
)
JsonNum
)
)
]
)
, '( "namedObject"
, Named "NamedElmRecord"
( JsonObject
'[ '("stringField", JsonString)
, '( "listOfStrings"
, JsonArray JsonString
)
]
)
)
]
)
This spec will produce the following code (after running it through elm-format
):
module Api.Data exposing
( ExampleType
, NamedElmRecord
, SumTypeWithAutomaticConstructorNames(..)
, SumTypeWithCustomConstructorNames(..)
, exampleTypeDecoder
, exampleTypeEncoder
, namedElmRecordDecoder
, namedElmRecordEncoder
, sumTypeWithAutomaticConstructorNamesDecoder
, sumTypeWithAutomaticConstructorNamesEncoder
, sumTypeWithCustomConstructorNamesDecoder
, sumTypeWithCustomConstructorNamesEncoder
)
import Iso8601
import Json.Decode
import Json.Encode
import Time
exampleTypeDecoder : Json.Decode.Decoder ExampleType
exampleTypeDecoder =
Json.Decode.succeed
(\a b c ->
{ stringField = a
, anonymousObject = b
, namedObject = c
}
)
|> Json.Decode.andThen (\a -> Json.Decode.map a Json.Decode.string)
|> Json.Decode.andThen
(\a ->
Json.Decode.map a
(Json.Decode.succeed
(\b c d e ->
{ floatField = b
, dateField = c
, sumType1 = d
, sumType2 = e
}
)
|> Json.Decode.andThen (\b -> Json.Decode.map b Json.Decode.float)
|> Json.Decode.andThen (\b -> Json.Decode.map b Iso8601.decoder)
|> Json.Decode.andThen (\b -> Json.Decode.map b sumTypeWithCustomConstructorNamesDecoder)
|> Json.Decode.andThen (\b -> Json.Decode.map b sumTypeWithAutomaticConstructorNamesDecoder)
)
)
|> Json.Decode.andThen (\a -> Json.Decode.map a namedElmRecordDecoder)
exampleTypeEncoder : ExampleType -> Json.Encode.Value
exampleTypeEncoder a =
Json.Encode.object
[ ( "stringField", Json.Encode.string a.stringField )
, ( "anonymousObject"
, (\b ->
Json.Encode.object
[ ( "floatField", Json.Encode.float b.floatField )
, ( "dateField", Iso8601.encode b.dateField )
, ( "sumType1", sumTypeWithCustomConstructorNamesEncoder b.sumType1 )
, ( "sumType2", sumTypeWithAutomaticConstructorNamesEncoder b.sumType2 )
]
)
a.anonymousObject
)
, ( "namedObject", namedElmRecordEncoder a.namedObject )
]
namedElmRecordDecoder : Json.Decode.Decoder NamedElmRecord
namedElmRecordDecoder =
Json.Decode.succeed (\a b -> { stringField = a, listOfStrings = b })
|> Json.Decode.andThen (\a -> Json.Decode.map a Json.Decode.string)
|> Json.Decode.andThen (\a -> Json.Decode.map a (Json.Decode.list Json.Decode.string))
namedElmRecordEncoder : NamedElmRecord -> Json.Encode.Value
namedElmRecordEncoder a =
Json.Encode.object
[ ( "stringField", Json.Encode.string a.stringField )
, ( "listOfStrings", Json.Encode.list Json.Encode.string a.listOfStrings )
]
sumTypeWithAutomaticConstructorNamesDecoder : Json.Decode.Decoder SumTypeWithAutomaticConstructorNames
sumTypeWithAutomaticConstructorNamesDecoder =
Json.Decode.oneOf
[ Json.Decode.map SumTypeWithAutomaticConstructorNames_1 Json.Decode.int
, Json.Decode.map SumTypeWithAutomaticConstructorNames_2 Json.Decode.string
, Json.Decode.map SumTypeWithAutomaticConstructorNames_3 Json.Decode.float
]
sumTypeWithAutomaticConstructorNamesEncoder : SumTypeWithAutomaticConstructorNames -> Json.Encode.Value
sumTypeWithAutomaticConstructorNamesEncoder a =
case a of
SumTypeWithAutomaticConstructorNames_1 b ->
Json.Encode.int b
SumTypeWithAutomaticConstructorNames_2 b ->
Json.Encode.string b
SumTypeWithAutomaticConstructorNames_3 b ->
Json.Encode.float b
sumTypeWithCustomConstructorNamesDecoder : Json.Decode.Decoder SumTypeWithCustomConstructorNames
sumTypeWithCustomConstructorNamesDecoder =
Json.Decode.oneOf
[ Json.Decode.map IntConstructor Json.Decode.int
, Json.Decode.map StringConstructor Json.Decode.string
, Json.Decode.map FloatConstructor Json.Decode.float
]
sumTypeWithCustomConstructorNamesEncoder : SumTypeWithCustomConstructorNames -> Json.Encode.Value
sumTypeWithCustomConstructorNamesEncoder a =
case a of
IntConstructor b ->
Json.Encode.int b
StringConstructor b ->
Json.Encode.string b
FloatConstructor b ->
Json.Encode.float b
type SumTypeWithAutomaticConstructorNames
= SumTypeWithAutomaticConstructorNames_1 Int
| SumTypeWithAutomaticConstructorNames_2 String
| SumTypeWithAutomaticConstructorNames_3 Float
type SumTypeWithCustomConstructorNames
= IntConstructor Int
| StringConstructor String
| FloatConstructor Float
type alias ExampleType =
{ stringField : String
, anonymousObject :
{ floatField : Float
, dateField : Time.Posix
, sumType1 : SumTypeWithCustomConstructorNames
, sumType2 : SumTypeWithAutomaticConstructorNames
}
, namedObject : NamedElmRecord
}
type alias NamedElmRecord =
{ stringField : String, listOfStrings : List String }
Generating code
The main function exposed by this module is elmDefs
, which returns a Set Definition
(where Definition
is from the elm-syntax
package). For examples on how to transform a Definition
into some files on disk, see /test/test.hs
.