vitiral
vitiral

Reputation: 9218

How to convert from String to Int in Json.Decoder in elm-core > 5.0.0

This is idential to this question, except elm has changed since then so that answer is no longer valid (in particular there is no longer a Decode.customDecoder object).

How do you do this same thing in elm-core > 5.0.0?

Upvotes: 4

Views: 1521

Answers (6)

instant
instant

Reputation: 696

Here's a solution that avoids having to choose a sketchy default value:

import Json.Decode as Decode exposing (Decoder)

decodeIntString : Decoder (Maybe Int)
decodeIntString =
    Decode.map String.toInt Decode.string


decodeMaybeFail : Maybe a -> Decoder a
decodeMaybeFail val =
    case val of
        Just a ->
            Decode.succeed a

        Nothing ->
            Decode.fail "'Nothing' value can't be decoded"

You then use it with

Decode.andThen decodeMaybeFail decodeIntString

(this is of type Decoder Int, so you use it like you would use Decode.int)

Not obvious at first, but it illustrates how to break this problem down into simple small steps.

In my opinion a function decodeMaybeFail : Maybe a -> Decoder a should be in the standard Json.Decode module.

Upvotes: 0

Berend de Boer
Berend de Boer

Reputation: 2112

For Elm 0.19 using the documentation for targetValue, the code becomes:

import Html.Events
import Json.Decode

onIntInput : (Int -> msg) -> Attribute msg
onIntInput tagger =
    Html.Events.stopPropagationOn "input" <|
        Json.Decode.map alwaysStop (Json.Decode.map tagger targetIntValue)

targetIntValue : Decoder Int
targetIntValue =
  Json.Decode.at ["target", "value"] Json.Decode.string |> Json.Decode.andThen (\value ->
    case String.toInt value of
      Just number -> Json.Decode.succeed number
      Nothing -> Json.Decode.fail "not a number")

alwaysStop : a -> (a, Bool)
alwaysStop x =
  (x, True)

and you can then use onIntInput where you used onInput.

Upvotes: 0

rofrol
rofrol

Reputation: 15266

Use parseInt decoder (source):

decodeString parseInt """ "123" """

Look at the tutorial about custom decoders, like for date. Reuse fromResult approach.

Read about function composition (<<), which is used in parseInt function.

Upvotes: 2

Kasey Speakman
Kasey Speakman

Reputation: 4641

You can create your own custom decoder by using succeed and fail from Json.Decode. I changed the argument order below to make it chainable.

Edit: separated decoder concerns from result concerns.

import Json.Decode as Json
import Result


-- // This is a Result helper function that could be used elsewhere.
-- // Currently, there is no Result.either in Elm core.
eitherR : (x -> b) -> (a -> b) -> Result x a -> b
eitherR fErr fOk result =
    case result of
        Err x ->
            fErr x

        Ok a ->
            fOk a


customDecoder : (a -> Result String b) -> Json.Decoder a -> Json.Decoder b
customDecoder fResult decoder =
    decoder |> Json.andThen (fResult >> eitherR Json.fail Json.succeed)

Plugging this in to the linked question...

let number =
    Json.oneOf [ Json.int, Json.string |> customDecoder String.toInt ]

Here's another example use. This is a version of onChange which converts the value to an integer. Mainly useful for select when you know the option value is an Int.

import Html
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import String


onChangeInt : (Int -> msg) -> Attribute msg
onChangeInt fMsg =
    on "change" (targetValue |> customDecoder String.toInt |> Json.map fMsg)

Note that targetValue is defined in the Html.Events module:

targetValue : Decoder String

Upvotes: 3

Simon H
Simon H

Reputation: 21007

Given that in the comment to the other answer, you state the need to embrace possible failure, you could use a Maybe.

stringIntDecoder : Decoder (Maybe Int)
stringIntDecoder =
    Json.Decode.map (String.toInt >> Result.toMaybe) Json.Decode.string

Or

stringIntDecoder : Decoder (Result String Int)
stringIntDecoder =
    Json.Decode.map (String.toInt) Json.Decode.string

Upvotes: 1

Ryan Plant
Ryan Plant

Reputation: 1047

One way to do it (as of Elm 0.18 and core 5.0) would be like this:

stringIntDecoder : Decoder Int
stringIntDecoder =
    Json.Decode.map (\str -> String.toInt (str) |> Result.withDefault 0) Json.Decode.string

The String.toInt function from the standard library takes a string and attempts to convert it to an integer, returning a Result. Result.withDefault does what its name implies -- you give it some default value and a result, and if the Result is Ok x you get x but if it's Err _ you get the default value you supplied, here 0. You could instead write a function to handle a Result yourself if you like and pass that function in instead.

Upvotes: 6

Related Questions