The Json.Decode.Ancillary
module provides additional, convenient Json
decoding functionality not found in Json.Decode
or Json.Decode.Extra
.
Decode simple types from JSON with additional restrictions.
nonemptyString : Json.Decode.Decoder String
Decode a JSON String
to Elm but fail if the string is empty.
decodeString nonemptyString "true" == Err ...
decodeString nonemptyString "42" == Err ...
decodeString nonemptyString "3.14" == Err ...
decodeString nonemptyString "\"hello\"" == Ok "hello"
decodeString nonemptyString "\" \"" == Ok " "
decodeString nonemptyString "\"\"" == Err ...
decodeString nonemptyString "{ \"hello\": 42 }" == Err ...
Create custom decoders that validate additional characteristics of the decoded values.
validate : (a -> Basics.Bool) -> String -> Json.Decode.Decoder a -> Json.Decode.Decoder a
Given a predicate to validate a decoded value and an error message for
invalid values, decode a value from JSON and fail if it does not evaluate to
True
.
Examples:
nonemptyString : Decoder String
nonemptyString =
Decode.string
|> validate (not << String.isEmpty) "Expected a nonempty string!"
mapMaybe : (a -> Maybe b) -> String -> Json.Decode.Decoder a -> Json.Decode.Decoder b
Given a function to map a value a
into Maybe b
and an error message for
invalid values, decode an a
value from JSON and convert it into a b
or fail.
firstChar : Decoder Char
firstChar =
Decode.string
|> mapMaybe (Maybe.map Tuple.first << String.uncons) "Expected a nonempty string!"
mapResult : (a -> Result String b) -> Json.Decode.Decoder a -> Json.Decode.Decoder b
Given a function to map a value a
into Result String b
, decode an a
value from JSON and convert it into a b
or fail with the error message.
customParser : Parser SomeType
customParser =
...
customDecoder : Decoder SomeType
customDecoder =
Decode.string
|> mapResult (Result.mapError Parser.deadEndsToString << Parser.run customParser )
It's not uncommon to want to import JSON representations of custom types, in which custom types are typically represented by strings, e.g.
{
"animals": ["dog", "cat", "cat"]
}
->
animals : List Animal
animals =
[ Dog, Cat, Cat ]
or
{
"animals": [{
"dog": "Spot"
}, {
"cat": "Bastet"
}, {
"dog": "Rover"
}]
}
->
animals : List Animal
animals =
[ Dog "Spot"
, Cat "Bastet"
, Dog "Rover"
]
These functions can help handle such JSON, though they may have applications beyond mere custom type handling.
lookup : Dict String b -> Json.Decode.Decoder b
Given an association dictionary between strings and a type, decode a string from JSON into the associated value or fail if none match.
animalDecoder : Decoder Animal
animalDecoder =
lookup
(Dict.fromList
[ ( "dog", Dog )
, ( "cat", Cat )
, ( "bird", Bird )
]
)
decodeString animalDecoder "true" == Err ...
decodeString animalDecoder "42" == Err ...
decodeString animalDecoder "3.14" == Err ...
decodeString animalDecoder "\"hello\"" == Err ...
decodeString animalDecoder "\"dog\"" == Ok Dog
decodeString animalDecoder "\"bird\"" == Ok Bird
lookupComparable : Dict comparable a -> Json.Decode.Decoder comparable -> Json.Decode.Decoder a
Given an association dictionary between two types, decode the first type from JSON into the associated value of the second type or fail if none match.
Used lookup
if your comparable
type is String
, the more common use-case.
asciiDecoder : Decoder Char
asciiDecoder =
lookupComparable
(Dict.fromList
[
, ...
, ( 65, 'A' )
, ( 66, 'B' )
, ( 67, 'C' )
, ...
)
Decode.int
decodeString asciiDecoder "true" == Err ...
decodeString asciiDecoder "66" == Ok 'B'
decodeString asciiDecoder "99999" == Err ...
decodeString asciiDecoder "3.14" == Err ...
decodeString asciiDecoder "\"hello\"" == Err ...
lookupWith : List ( a, b ) -> Json.Decode.Decoder a -> Json.Decode.Decoder b
Less-efficient alternative (except perhaps with short lists) to
lookupComparable
for non-comparable values. Given an association list between
two types, decode the first type from JSON into the associated value of the
second type or fail if none match.
You should probably use lookup
or lookupComparable
if possible, unless your
list is very short.
asciiDecoder : Decoder Char
asciiDecoder =
lookupWith
[
, ...
, ( 65, 'A' )
, ( 66, 'B' )
, ( 67, 'C' )
, ...
]
Decode.int
decodeString asciiDecoder "true" == Err ...
decodeString asciiDecoder "66" == Ok 'B'
decodeString asciiDecoder "99999" == Err ...
decodeString asciiDecoder "3.14" == Err ...
decodeString asciiDecoder "\"hello\"" == Err ...
mapByField : List ( String, a -> b ) -> Json.Decode.Decoder a -> Json.Decode.Decoder b
Given an association list of fields (as strings) and functions mapping one type to another, and a decoder of the first type, map the contents of a matching field with the associated map.
This is a little complicated sounding, so consider the following example:
type Species
= Dog
| Cat
| Bird
type alias Animal =
{ species : Species
, age : Int
}
animalDecoder : Decoder Animal
animalDecoder =
mapByField
[ ( "dog", Animal Dog )
, ( "cat", Animal Cat )
, ( "bird", Animal Bird )
]
Decode.int
decodeString animalDecoder "true" == Err ...
decodeString animalDecoder "42" == Err ...
decodeString animalDecoder "{\"dog\": 6}" == Ok ( Dog 42 )
decodeString animalDecoder "{\"bird\": 10}" == Ok ( Bird 10 )
decodeString animalDecoder "\"dog\"" == Err ...
decodeString animalDecoder "{\"dog\": \"hello\"}" == Err ...