Decode a given string into an Elm value based on the
provided Decoder. This will fail if the string is not
well-formed YAML or if the Decoder doesn't match the
input.
fromString int "4" --> Ok 4
fromString int "hello" --> Err (Decoding "Expected int, got: \"hello\" (string)")Represents a YAML tree.
A structured error describing how a decoder failed.
Decode a YAML string into an Elm String.
fromString string "true"
--> Err (Decoding "Expected string, got: True (bool)")
fromString string "'true'" --> Ok "true"
fromString string "42"
--> Err (Decoding "Expected string, got: 42 (int)")
fromString string "'42'" --> Ok "42"
fromString string "hello" --> Ok "hello"
fromString string "hello: 42"
--> Err (Decoding "Expected string, got: { hello: 42 (int) } (map)")Decode a YAML boolean into an Elm Bool.
fromString bool "true" --> Ok True
fromString bool "'true'"
--> Err (Decoding "Expected bool, got: \"true\" (string)")
fromString bool "42"
--> Err (Decoding "Expected bool, got: 42 (int)")
fromString bool "hello"
--> Err (Decoding "Expected bool, got: \"hello\" (string)")
fromString bool "hello: 42"
--> Err (Decoding "Expected bool, got: { hello: 42 (int) } (map)")Decode a YAML number into an Elm Int.
fromString int "true"
--> Err (Decoding "Expected int, got: True (bool)")
fromString int "'true'"
--> Err (Decoding "Expected int, got: \"true\" (string)")
fromString int "42" --> Ok 42
fromString int "'42'"
--> Err (Decoding "Expected int, got: \"42\" (string)")
fromString int "3.14"
--> Err (Decoding "Expected int, got: 3.14 (float)")
fromString int "hello"
--> Err (Decoding "Expected int, got: \"hello\" (string)")
fromString int "hello: 42"
--> Err (Decoding "Expected int, got: { hello: 42 (int) } (map)")Decode a YAML number into an Elm Float.
fromString float "true"
--> Err (Decoding "Expected float, got: True (bool)")
fromString float "'true'"
--> Err (Decoding "Expected float, got: \"true\" (string)")
fromString float "42" --> Ok 42
fromString float "'42'"
--> Err (Decoding "Expected float, got: \"42\" (string)")
fromString float "3.14" --> Ok 3.14
fromString float "hello"
--> Err (Decoding "Expected float, got: \"hello\" (string)")
fromString float "hello: 42"
--> Err (Decoding "Expected float, got: { hello: 42 (int) } (map)")Decode a YAML null value into Nothing.
fromString null "" --> Ok Nothing
fromString null "null" --> Ok Nothing
fromString null "true"
--> Err (Decoding "Expected null, got: True (bool)")
fromString null "42"
--> Err (Decoding "Expected null, got: 42 (int)")
fromString null "hello: 42"
--> Err (Decoding "Expected null, got: { hello: 42 (int) } (map)")Decode a nullable YAML value into an Elm value.
fromString (nullable int) "42" --> Ok (Just 42)
fromString (nullable int) "3.14"
--> Err (Decoding "Expected int, got: 3.14 (float)")
fromString (nullable int) "null" --> Ok Nothing
fromString (nullable int) "true"
--> Err (Decoding "Expected int, got: True (bool)")Decode a YAML array into an Elm List.
fromString (list int) "[1,2,3]" --> Ok [ 1, 2, 3 ]
fromString (list bool) "[ true, false ]" --> Ok [ True, False ]
fromString (list float) "true"
--> Err (Decoding "Expected list, got: True (bool)")Decode a YAML record into an Elm Dict.
import Dict exposing (Dict)
fromString (dict int) "{ alice: 42, bob: 99 }"
--> Ok (Dict.fromList [ ( "alice", 42 ), ( "bob", 99 ) ])
fromString (dict string) "42"
--> Err (Decoding "Expected record, got: 42 (int)")Decode a YAML record, requiring a particular field.
fromString (field "x" int) "{ x: 3 }" --> Ok 3
fromString (field "x" int) "{ x: 3, y: 4 }" --> Ok 3
fromString (field "x" int) "{ x: true }" --> Err (Decoding "Expected int, got: True (bool)")
fromString (field "x" int) "{ y: 4 }" --> Err (Decoding "Expected property: x")
fromString (field "name" string) "{ name: Tom }" --> Ok "Tom"The record can have other fields. Lots of them! The only thing this decoder
cares about is if x is present and that the value there can be decoded.
Check out mapN and andMap to see how to decode multiple fields!
Decode a nested YAML record, requiring certain fields.
yaml : String
yaml = """{ person: { name: Tom, age: 42 } }"""
fromString (at ["person", "name"] string) yaml --> Ok "Tom"
fromString (at ["person", "age"] int) yaml --> Ok 42
fromString (at ["not", "here"] int) yaml --> Err (Decoding "Expected property: not")This is really just shorthand for field. Equivalent to
saying things like:
fromString (field "person" (field "name" string)) yaml
--> fromString (at [ "person", "name" ] string) yamlTry a list of different decoders. Pick the first working one.
This can be useful if the YAML comes in different formats. For
example, if you want to read an array of numbers but some of them
are null.
fromString (oneOf [ field "a" int, field "b" int ]) "{a: 42}" --> Ok 42
fromString (oneOf [ field "a" int, field "b" int ]) "{b: 3}" --> Ok 3
fromString (oneOf [ field "a" int, field "b" int ]) "true" --> Err (Decoding "Empty")Makes its argument optional.
A decoder which returns Nothing when it fails.
Helpful when dealing with optional fields: you probably want to
use maybe outside field or at. Here are a few examples:
yaml : String
yaml = "{ name: Stacy, age: 27, temperature: 37.6 }"
fromString (maybe (field "age" int)) yaml --> Ok (Just 27)
fromString (maybe (field "height" float)) yaml --> Ok NothingThese two examples decode to Nothing if a field does not exist.
They say there may be an age field, if it exists it must
be an integer. And there may be a height field, if it exists
it must be a Float.
You can also decode to Nothing if a field is a different type:
fromString (field "temperature" (maybe string)) yaml --> Ok Nothing
fromString (field "age" (maybe int)) yaml --> Ok (Just 27)These two examples say you must have temperature and
age fields and the content may be integers.
Transform the result of a decoder. For example, get the length of a string:
stringLength : Decoder Int
stringLength =
map String.length string
fromString stringLength "hello" --> Ok 5map runs the decoder (string in the example above) and
gives the result to the function (String.length in the
example above).
Try two decoders and then combine the result. You can use this to decode records with 2 fields:
type alias Point = { x : Float, y : Float }
point : Decoder Point
point =
map2 Point
(field "x" float)
(field "y" float)
fromString point "{x: 1.2, y: 2.5}" --> Ok { x = 1.2, y = 2.5 }map2 runs each decoder in order and privides the results to the
function (taking 2 arguments; the Point constructor in the example
above).
Try three decoders and then combine the result.
Try four decoders and then combine the result.
Try five decoders and then combine the result.
Try six decoders and then combine the result.
Try seven decoders and then combine the result.
Try eight decoders and then combine the result.
Can be useful when decoding large objects incrementally.
For example, this can be used to to mapN for any number of arguments. See the
documentation for Json.Decode.Extra.andMap for a
thorough explanation of how to use andMap.
-- map4
sumFields : Decoder Int
sumFields =
succeed (\a b c d -> a + b + c + d)
|> andMap (field "a" int)
|> andMap (field "b" int)
|> andMap (field "c" int)
|> andMap (field "d" int)
fromString sumFields "{a: 2, b: 5, c: -3, d: 9}" --> Ok 13Decode YAML with a recursive (nested) structure.
An example is nested comments:
type alias Comment =
{ comment : String
, responses : Responses
}
type Responses = Responses (List Comment)
comment : Decoder Comment
comment =
map2 Comment
(field "comment" string)
(field "responses" (map Responses (list (lazy (\_ -> comment)))))
yaml : String
yaml = "{ comment: 'hello world', responses: [ {comment: 'hello', responses: [] } ] }"
fromString comment yaml
--> Ok { comment = "'hello world'", responses = Responses [{ comment = "'hello'", responses = Responses [] }] }By using lazy you make sure that the decoder only expands
to be as deep as the YAML structure. You can read more about
recursive data structures
here.
Do not do anything with a YAML value, just bring it into
Elm as a Value. This can be useful if you have particularly
complex data that you would like to deal with later. Or if you
are going to send it out of a port and do not care about its
structure.
Ignore the YAML and make the decoder fail. This is handy
when used with oneOf or andThen where you want to give a
custom error message in some case.
See the andThen docs for an example.
Ignore the YAML and produce a given Elm value.
fromString (succeed 42) "true" --> Ok 42
fromString (succeed 42) "[]" --> Ok 42
fromString (succeed 42) "{ " |> Result.toMaybe --> Nothing -- Long parsing errorCreate decoders that depend on previous results.
For example, if you decoding depends on a version
field:
type alias Info =
{ data: Int }
info : Decoder Info
info =
field "version" int
|> andThen infoHelp
-- infoHelp takes the "version" integer as its argument
infoHelp : Int -> Decoder Info
infoHelp version =
case version of
4 ->
infoDecoder4
3 ->
infoDecoder3
_ ->
fail ("Version "
++ String.fromInt version
++ " is not supported.")
infoDecoder4 : Decoder Info
infoDecoder4 =
map Info (field "v4data" int)
infoDecoder3 : Decoder Info
infoDecoder3 =
map (Info << String.length) (field "v3data" string)
fromString info "{ version: 3, v3data: test }"
--> Ok (Info 4)
fromString info "{ version: 4, v4data: 5 }"
--> Ok (Info 5)
fromString info "{ version: 1 }"
--> Err (Decoding "Version 1 is not supported.")Convert a Result into a decoder.
This can be useful when you want to use functions that operate on Result in decoders,
in combination with andThen.
validate : String -> Result String String
validate s =
case s of
"" -> Err "is empty"
_ -> Ok s
fromString (string |> andThen (validate >> fromResult)) "\"not empty\"" --> Ok "not empty"
fromString (string |> andThen (validate >> fromResult)) "" --> Err (Decoding "is empty")Convert a Maybe into a decoder.
This can be useful when you want to use functions that operate on Maybe in decoders,
in combination with andThen.
fromString (string |> andThen (String.toInt >> fromMaybe "Not an int")) "\"42\"" --> Ok 42
fromString (string |> andThen (String.toInt >> fromMaybe "Not an int")) "" --> Err (Decoding "Not an int")
A value that knows how to decode YAML.
There is a whole section in guide.elm-lang.org about decoders, so check it out for a more comprehensive introduction!