问题描述
我正在学习Elm,并且碰上了JSON解码器。我正在构建一个Elm前端,我想从后端API馈送URL,该API返回以下URL字符串的JSON数组:
["http://www.example.com?param=value","http://www.example2.com?param=value"]
如果我尝试使用Elm中的“列表字符串”解码器解析此字符串,则会失败,因为上面的内容未作为字符串引用或转义。我的示例解码器:
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"]
之所以失败,是因为在Elm中将JSON数组视为一个字符串列表,而不是Json.Decode.decodeString函数接受的平面字符串。如果我要引用并转义数组以与该解码器一起使用,则它将采用以下格式:
"[\"http://www.example.com?param=value\",\"http://www.example2.com?param=value\"]"
如何编写Elm解码器,以我的API返回的格式解析未引用/未转义的JSON数组?
更新 我认为我未能很好地传达我的问题;为此我深表歉意。 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调用在将其解析为解码器之前将其转换为JSON吗?还是由于对Elm的经验不足,我只是错过了明显的东西?如果可以进一步说明,请告诉我,谢谢您的帮助!
最终编辑 事实证明,在使用罗伯特的出色示例之后,我自己由于一个不相关的问题而失败了。作为Elm的Http包的新手,我不知道Http.NetworkError消息,Robert的Http错误函数处理该消息。这导致我进入this question,并最终发现API中的CORS配置错误。
解决方法
Elm模块中不能存在裸JSON。编写要在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
}