Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add function to decode the contetns of a JSON string with another decoder #35

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 49 additions & 2 deletions src/Json/Decode.elm
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module Json.Decode exposing
( Decoder, string, bool, int, float
( Decoder, string, stringAs, bool, int, float
, nullable, list, array, dict, keyValuePairs, oneOrMore
, field, at, index
, maybe, oneOf
Expand All @@ -23,7 +23,7 @@ JSON decoders][guide] to get a feel for how this library works!
@docs field, at, index

# Inconsistent Structure
@docs maybe, oneOf
@docs maybe, oneOf, stringAs

# Run Decoders
@docs decodeString, decodeValue, Value, Error, errorToString
Expand Down Expand Up @@ -75,6 +75,53 @@ string =
Elm.Kernel.Json.decodeString


{-| Decode the contents a JSON string with another decoder. This can be useful
if the JSON contains values that are really numbers, but exists as strings. For
example, say you want to decode the list stringified numbers.

sneakyFloat : Decoder Float
sneakyFloat =
stringAs float

-- decodeString (list sneakyFloat) "[\"1.5\", \"42.0\"]" == Ok [1.5, 42.0]

Unfortunately, there could be times like this when you have to work with JSON
that uses strings to represent floats instead of just regular JSON numbers. If
you will need to do actual arithmetic on them, like multiplying them together,
then this functions allows you to parse them correctly with ease.

It can be used together with `oneOf` to handle cases when a number is sometimes
represented by a JSON string and sometimes an actual JSON number.

NOTE: When the these values are integers as strings, then there is a valid
reason NOT to decode them into integers. JavaScript uses floating point types
to represent integers as well, so there is no guarantee that it will be exact.
For example for unique IDs, it is better to use strings in JavaScript.
-}
stringAs : Decoder value -> Decoder value
stringAs decoder =
string
|> andThen
(\str ->
case decodeString decoder str of
Err err ->
failWithError err

Ok val ->
succeed val
)


{-| TODO: write a proper version of this function.
Depends on [#25][issue-25]

[issue-25]: https://github.com/elm/json/issues/25
-}
failWithError : Error -> Decoder a
failWithError =
fail << errorToString


{-| Decode a JSON boolean into an Elm `Bool`.

decodeString bool "true" == Ok True
Expand Down
25 changes: 25 additions & 0 deletions tests/Json.elm
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ tests : Test
tests =
describe "Json decode"
[ intTests
, stringAsTests
, customTests
]

Expand Down Expand Up @@ -54,6 +55,30 @@ intTests =
]


stringAsTests : Test
stringAsTests =
let
testStringAs decoder val str =
case Json.decodeString (Json.stringAs decoder) str of
Ok _ ->
Expect.equal val True

Err _ ->
Expect.equal val False
int = Json.int
float = Json.float
in
describe "Jsong decode stringAs"
[ test "int string -> int" <| testStringAs int True "\"3\""
, test "float string -> int" <| testStringAs int False "\"3.0\""
, test "int -> int" <| testStringAs int False "3"
, test "float -> int" <| testStringAs int False "3.0"
, test "int string -> float" <| testStringAs float False "\"3\""
, test "float string -> float" <| testStringAs float True "\"3.0\""
, test "int -> float" <| testStringAs float False "3"
, test "float -> float" <| testStringAs float False "3.0"
]

customTests : Test
customTests =
let
Expand Down