如何在 elm-core > 5.0.0 中将 Json.Decoder 中的 String 转换为 Int

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

这与 相同,只是 elm 从那时起发生了变化,因此答案不再有效(特别是不再有 Decode.customDecoder 对象)。

如何在 elm-core > 5.0.0 中做同样的事情?

一种方法(从 Elm 0.18 和核心 5.0 开始)是这样的:

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

标准库中的 String.toInt 函数接受一个字符串并尝试将其转换为整数,并返回一个结果。 Result.withDefault 就像它的名字所暗示的那样——你给它一些默认值和一个结果,如果结果是 Ok x 你得到 x 但如果它是 Err _ 你得到您提供的默认值,此处为 0。如果愿意,您可以自己编写一个函数来处理结果,然后传递该函数。

鉴于在对其他答案的评论中,您声明需要接受可能的失败,您可以使用 Maybe。

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

或者

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

您可以使用 Json.Decode 中的 succeedfail 创建自己的自定义解码器。我更改了下面的参数顺序以使其可链接。

编辑:将解码器问题与结果问题分开。

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)

将其插入链接的问题...

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

这是另一个使用示例。这是 onChange 的一个版本,它将值转换为整数。当您知道选项值是 Int 时,主要对 select 有用。

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)

注意targetValue定义在Html.Events模块中:

targetValue : Decoder String

使用parseInt decoder (source):

decodeString parseInt """ "123" """

查看 tutorial about custom decoders, like for date. Reuse fromResult 方法。

阅读 function composition (<<),它在 parseInt 函数中使用。

对于使用 targetValue 文档的 Elm 0.19,代码变为:

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)

然后您可以在使用 onInput 的地方使用 onIntInput。

这是一个避免必须选择粗略默认值的解决方案:

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"

然后您将它与

一起使用
Decode.andThen decodeMaybeFail decodeIntString

(这是 Decoder Int 类型,因此您可以像使用 Decode.int 一样使用它)

起初并不明显,但它说明了如何将这个问题分解成简单的小步骤。

我认为函数 decodeMaybeFail : Maybe a -> Decoder a 应该在标准 Json.Decode 模块中。