Elm JSON 非字符串 JSON 数组的解码器

Elm JSON Decoder for non-string JSON Array

我正在学习 Elm,并且 运行 已经用 JSON 个解码器撞墙了。我正在构建一个 Elm 前端,我想从后端 API 提供 URLs,其中 returns 以下 JSON 数组 URL 字符串:

["http://www.example.com?param=value","http://www.example2.com?param=value"]

如果我尝试在 Elm 中使用 List String 解码器解析它,它会失败,因为上面没有作为字符串引用或转义。我的示例解码器:

stringListDecoder : Json.Decode.Decoder (List String)
stringListDecoder = Json.Decode.list Json.Decode.string

myArrDec2 = D.list D.string
D.decodeString myArrDec2 ["http://www.example.com?param=value","http://www.example2.com?param=value"]

这会失败,因为 JSON 数组在 Elm 中被视为字符串列表,而不是 Json.Decode.decodeString 函数接受的扁平字符串。如果我要引用和转义数组以使用此解码器,它将采用以下格式:

"[\"http://www.example.com?param=value\",\"http://www.example2.com?param=value\"]"

我如何编写一个 Elm 解码器来解析 unquote/unescaped JSON 数组,格式为我的 API?


更新 我想我没有很好地表达我的问题;为此,我深表歉意。 API 返回此值(有效 JSON):

["http://www.example.com?param=value","http://www.example2.com?param=value"]

我正在用 Http.get 抓取这个 JSON 并尝试 运行 结果的解码器,但失败了,因为它不是字符串。

-- HTTP
getUrls : Cmd Msg
getUrls =
Http.get
 { url = "http://127.0.0.1:3000/request" -- This is the endpoint returning ["http://www.example.com?param=value","http://www.example2.com?param=value"]
 , expect = Http.expectJson GotUrls urlDecoder
 }


urlDecoder : Decoder (List String)
urlDecoder =
 Json.Decode.list Json.Decode.string

由于 Elm 无法接受这个 JSON 而不将其转义为字符串,我可以使用 Http.get 调用将其转换为一个,然后再使用我的解码器进行解析吗?还是由于我对 Elm 缺乏经验而遗漏了一些明显的东西?如果我可以进一步澄清,请告诉我,感谢您的帮助!


最终编辑 在使用了 Robert 的优秀示例后,结果证明我自己的示例由于不相关的问题而失败。作为 Elm 的 HTTP 包的新手,我不知道 Robert 的 Http 错误函数处理的 Http.NetworkError 消息。这导致我 并最终在 API.

中揭示了 CORS 配置错误

Bare JSON 不能存在于 Elm 模块中。在 Elm 中编写要解码的示例 JSON 时,必须将其包装在其他表示形式中。

但是,该表示是特定于 Elm 的;它是 而不是 解码器期望从 API.

当后端发送文字 JSON ["a","b"].

时,您的解码器应该可以正常工作

在代码中:

module Example exposing (..)

import Json.Decode as JD


stringListDecoder : JD.Decoder (List String)
stringListDecoder =
    JD.list JD.string


{-| Bare JSON cannot exist inside Elm syntax; it must be wrapped in something
else. In this case, in a string.
-}
jsonFromApi : String
jsonFromApi =
    "[ \"http://www.example.com?param=value\", \"http://www.example2.com?param=value\" ]"


decoded : Result JD.Error (List String)
decoded =
    JD.decodeString stringListDecoder jsonFromApi


expectedResult : Result JD.Error (List String)
expectedResult =
    Result.Ok [ "http://www.example.com?param=value", "http://www.example2.com?param=value" ]


decodesAsExpected : Bool
decodesAsExpected =
    decoded == expectedResult

编辑:Here's an example 适合 运行 和 elm reactor

src/request.json

["http://www.example.com?param=value","http://www.example2.com?param=value"]

src/Main.elm

module Main exposing (..)

import Browser
import Html exposing (Html, code, div, p, pre, span, text)
import Http
import Json.Decode as JD


type alias Model =
    { data : RemoteData Http.Error (List String) }


type RemoteData err a
    = NotAsked
    | Loading
    | Loaded a
    | Failed err


type Msg
    = GotUrls (Result Http.Error (List String))


initialModel =
    { data = NotAsked }


getUrls : Cmd Msg
getUrls =
    Http.get
        { url = "/src/request.json"

        -- This is the endpoint returning ["http://www.example.com?param=value","http://www.example2.com?param=value"]
        , expect = Http.expectJson GotUrls urlDecoder
        }


urlDecoder : JD.Decoder (List String)
urlDecoder =
    JD.list JD.string


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        GotUrls result ->
            case result of
                Ok strings ->
                    ( { model | data = Loaded strings }, Cmd.none )

                Err error ->
                    ( { model | data = Failed error }, Cmd.none )


view : Model -> Html Msg
view model =
    div []
        (case model.data of
            NotAsked ->
                [ text "no data yet" ]

            Loading ->
                [ text "loading" ]

            Failed err ->
                [ text "Failed... ", show err ]

            Loaded strings ->
                strings |> List.map (\s -> p [] [ text s ])
        )


show : Http.Error -> Html Msg
show error =
    case error of
        Http.BadUrl string ->
            span [] [ text "Bad Url: ", text string ]

        Http.Timeout ->
            text "Timeout"

        Http.NetworkError ->
            text "Network error"

        Http.BadStatus int ->
            span [] [ text "Bad Status: ", int |> String.fromInt |> text ]

        Http.BadBody string ->
            span [] [ text "Bad Body: ", pre [] [ code [] [ text string ] ] ]


main : Program () Model Msg
main =
    Browser.element
        { init = always ( initialModel, getUrls )
        , view = view
        , update = update
        , subscriptions = always Sub.none
        }