diff --git a/src/Json/Decode.gren b/src/Json/Decode.gren index 09dce07e..8550fee1 100644 --- a/src/Json/Decode.gren +++ b/src/Json/Decode.gren @@ -5,7 +5,7 @@ module Json.Decode exposing , maybe, oneOf , decodeString, decodeValue, Value, Error(..), errorToString , map, map2, map3, map4, map5, map6, map7, map8 - , lazy, value, null, succeed, fail, andThen + , lazy, value, null, succeed, fail, andThen, andMap ) {-| Turn JSON values into Gren values. We've inherited this from Elm. Definitely check out this [intro to @@ -44,7 +44,7 @@ JSON decoders][guide] to get a feel for how this library works! ## Fancy Decoding -@docs lazy, value, null, succeed, fail, andThen +@docs lazy, value, null, succeed, fail, andThen, andMap -} @@ -716,6 +716,35 @@ andThen = Gren.Kernel.Json.andThen +{-| Use andMap to incrementally decode a JSON object. Each andMap +provides a value that will be an argument to a type constructor. +For example: + + -- The Type + type alias Info = + { height : Float + , age : Int + } + + -- The Constructor + makeInfo : Float -> Int -> Info + makeInfo height age = + { height = height + , age = age + } + + -- The Decoder + infoDecoder : Decoder Info + infoDecoder = + succeed makeInfo + |> andMap (field "height" float) + |> andMap (field "age" int) + +-} +andMap : Decoder a -> Decoder (a -> b) -> Decoder b +andMap = + map2 (|>) + {-| Sometimes you have JSON with recursive structure, like nested comments. You can use `lazy` to make sure your decoder unrolls lazily. diff --git a/tests/src/Test/Json.gren b/tests/src/Test/Json.gren index a3b0368f..f0c83619 100644 --- a/tests/src/Test/Json.gren +++ b/tests/src/Test/Json.gren @@ -13,6 +13,7 @@ tests = describe "Json decode" [ intTests , customTests + , andMapTests ] @@ -85,3 +86,67 @@ customTests = ++ Json.errorToString message in test "customDecoder preserves user error messages" <| \{} -> assertion + +-- Used for testing andMap +type alias TestRecord1 = + { stringField : String + , intField : Int + , boolField: Bool + , floatField: Float + , stringArrayField: Array String + } + +-- The constructor for TestRecord1 +mkTestRecord1: String -> Int -> Bool -> Float -> Array String -> TestRecord1 +mkTestRecord1 s i b f sa = + { stringField = s + , intField = i + , boolField = b + , floatField = f + , stringArrayField = sa + } + +andMapTests : Test +andMapTests = + let + jsonString = + """ + { + "stringField": "bar", + "intField": 42, + "boolField": true, + "floatField": 1.5, + "stringArrayField": [ "a", "b" ] + } + """ + + myDecoder = + Json.succeed mkTestRecord1 + |> Json.andMap (Json.field "stringField" Json.string) + |> Json.andMap (Json.field "intField" Json.int) + |> Json.andMap (Json.field "boolField" Json.bool) + |> Json.andMap (Json.field "floatField" Json.float) + |> Json.andMap (Json.field "stringArrayField" (Json.array Json.string)) + + parse = Json.decodeString myDecoder jsonString + + record = when parse is + Ok r -> r + Err _ -> mkTestRecord1 "" 0 False 0.0 [] + + in + describe "Json andMap " + [test "string field" <| + \{} -> Expect.equal record.stringField "bar" + , test "int field" <| + \{} -> Expect.equal record.intField 42 + , test "bool field" <| + \{} -> Expect.equal record.boolField True + , test "float field" <| + \{} -> Expect.within (Expect.Relative 0.001) record.floatField 1.5 + , test "string-array field" <| + \{} -> Expect.equalArrays record.stringArrayField [ "a", "b" ] + , test "parse error" <| + \{} -> Expect.ok parse + + ]