【问题标题】:Read in data from JSON file从 JSON 文件中读取数据
【发布时间】:2015-12-18 14:32:12
【问题描述】:

假设我有一个位于 http://www.randomurl.com/jobs.json 的 JSON 文件,它看起来像这样:

{ "jobs": [
  { "task" : "turn burgers" ,
    "who" : "Anni" ,
    "place" : "Quick"}
  , 
  { "task" : "dishes" ,
    "who" : "Bob" ,
    "place" : "McDo"}
]}

我已经做了一个解码器:

type alias Job = {
  task : String
, who : String
, place: String 
}

type alias Jobs  = List Job

decoder : Decoder Job
decoder =
  Decode.object3 Job
    (Decode.at ["attributes", "task"] Decode.string)
    (Decode.at ["attributes", "who"] Decode.string)
    (Decode.at ["attributes", "place"] Decode.string)

decoderColl : Decoder Jobs
decoderColl =
  Decode.object1 identity
    ("jobs" := Decode.list decoder)

如何使用我的解码器从该网站读取文件?我想我需要 Http 包,但我不知道如何应用它。

【问题讨论】:

    标签: json elm


    【解决方案1】:

    首先 - 你的 decoder 功能有点偏离。没有中间的“属性”对象,所以你可以把它改成这样:

    decoder : Decoder Job
    decoder =
      Decode.object3 Job
        ("task" := Decode.string)
        ("who" := Decode.string)
        ("place" := Decode.string)
    

    您是正确的,您需要 elm-http 包。使用它,您可以创建一个Http.get 任务,将结果映射到一个动作。

    作为一个基本示例,让我们制作一个从 url 下拉作业列表的按钮。我们需要一个 GetJobs 动作来触发 HTTP 请求,以及一个 ShowJobs 动作,当请求成功返回时会被触发。

    假设我们的 Action 类型如下所示:

    type Action
      = NoOp
      | GetJobs
      | ShowJobs (Maybe Jobs)
    

    然后我们可以创建一个getJobs 函数来构建一个可以运行的任务。对于这个简单的示例,我们可以使用Task.toMaybe 来抑制任何 HTTP 或 JSON 解码错误。

    getJobs : Effects Action
    getJobs =
      Http.get decoderColl jobsUrl
        |> Task.toMaybe
        |> Task.map ShowJobs
        |> Effects.task
    

    为了将它们粘合在一起,我们将使用StartApp,因为它可以让我们使用任务和效果。这是一个可以在本地构建的工作示例,假设 jobs.json 存在于同一目录中。

    import Http
    import StartApp
    import Effects exposing (Effects,Never)
    import Task
    import Html exposing (..)
    import Html.Events exposing (..)
    import Json.Decode as Decode exposing (Decoder, (:=))
    
    jobsUrl = "./jobs.json"
    
    -- StartApp plumbing
    app =
      StartApp.start { init = init, view = view, update = update, inputs = [] }
    
    main =
      app.html
    
    port tasks : Signal (Task.Task Never ())
    port tasks =
      app.tasks
    
    
    type Action
      = NoOp
      | GetJobs
      | ShowJobs (Maybe Jobs)
    
    type alias Model =
      { jobs : Maybe Jobs }
    
    init =
      ({ jobs = Nothing }, Effects.none)
    
    update action model =
      case action of
        NoOp ->
          (model, Effects.none)
        GetJobs ->
          ({ model | jobs = Nothing }, getJobs)
        ShowJobs maybeJobs ->
          ({ model | jobs = maybeJobs }, Effects.none)
    
    view address model =
      div []
        [ button [ onClick address GetJobs ] [ text "Click to get jobs!" ]
        , viewJobs model.jobs
        ]
    
    viewJobs maybeJobs =
      let
        viewJob job =
          li [] [ text ("Task: " ++ job.task ++ "; Who: " ++ job.who ++ "; Place: " ++ job.place) ]
      in
        case maybeJobs of
          Nothing ->
            div [] [ text "No jobs to display. Try clicking the button" ]
          Just jobs ->
            ul [] (List.map viewJob jobs)
    
    -- This is the key to map the result of the HTTP GET to an Action
    -- Note: Task.toMaybe swallows any HTTP or JSON decoding errors
    getJobs : Effects Action
    getJobs =
      Http.get decoderColl jobsUrl
        |> Task.toMaybe
        |> Task.map ShowJobs
        |> Effects.task
    
    -- An alternative to Task.toMaybe which dumps error information to the console log
    toMaybeWithLogging : Task.Task x a -> Task.Task y (Maybe a)
    toMaybeWithLogging task =
      Task.map Just task `Task.onError` (\msg -> Debug.log (toString msg) (Task.succeed Nothing))
    
    -- The Job type aliases from the question
    type alias Job = {
      task : String
      , who : String
      , place: String 
    }
    
    type alias Jobs  = List Job
    
    -- The updated Job decoder
    decoder : Decoder Job
    decoder =
      Decode.object3 Job
        ("task" := Decode.string)
        ("who" := Decode.string)
        ("place" := Decode.string)
    
    decoderColl : Decoder Jobs
    decoderColl =
      Decode.object1 identity
        ("jobs" := Decode.list decoder)
    

    【讨论】:

    • 由于某种原因,当我尝试编译您的代码时,它会耗尽内存,以前从未有过。
    • 发现问题,需要将({ model | jobs = Nothing }, getJobs)改为({ model | jobs <- Nothing }, getJobs)=<- 有什么区别?
    • <- 最近在 0.16 版中在这种情况下更改为 =。听起来你只需要最新版本的 elm
    • 啊,好的。 :) 我已经测试了你的代码,它适用于本地,但它不适用于位于在线的文件。
    • 你是对的,存在跨域问题。我有另一个网址,它可以工作,谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-06
    • 2014-10-15
    • 1970-01-01
    相关资源
    最近更新 更多