榆树解码未知的json结构

tjb*_*982 10 json decode elm

我刚刚开始与Elm合作,使用我正在研究的Rest API进行一些前端原型设计.通常,API返回可以解码的"合理"数据结构,因为键和值类型是众所周知的,但是几种资源类型返回的data条目只有具有没有预定结构的原始json.

到目前为止我读过的所有东西似乎都假设你知道你正在解码的数据的结构,而在普通的js中,相对容易循环键并反映类型以确定它们应该如何在运行时处理.我还没有看到在Elm中处理这类数据的明确路径.

例如,

{
  "name":"foo",
  "data": {
    "bar": [{"baz":123}, "quux"]
  },
  ...
}
Run Code Online (Sandbox Code Playgroud)

我想知道目前是否可以data用类似的东西来解析条目的值

function go(obj)
    for key in keys(foo)
        if foo[key] is an object
            go(foo[k])
        else if foo[key] is an array
            map(go, foo[k])
        ...
Run Code Online (Sandbox Code Playgroud)

特别:

  1. 目前是否可以在Elm中处理未知的,可能深度嵌套的和异构的json数据?
  2. 如果是这样,你能否给出关于作者如何将这样的数据解码的关键概念或高级直觉?

Cha*_*ert 10

是的,可以编写通用解码器.您可以先定义一个包含所有可能的Json类型的联合类型:

type JsVal
  = JsString String
  | JsInt Int
  | JsFloat Float
  | JsArray (List JsVal)
  | JsObject (Dict String JsVal)
  | JsNull
Run Code Online (Sandbox Code Playgroud)

现在你可以Json.Decode.oneOf尝试各种可能性.

import Json.Decode as D exposing (Decoder)
import Dict exposing (Dict)

jsValDecoder : Decoder JsVal
jsValDecoder =
  D.oneOf
    [ D.string |> D.andThen (D.succeed << JsString)
    , D.int |> D.andThen (D.succeed << JsInt)
    , D.float |> D.andThen (D.succeed << JsFloat)
    , D.list (D.lazy (\_ -> jsValDecoder)) |> D.andThen (D.succeed << JsArray)
    , D.dict (D.lazy (\_ -> jsValDecoder)) |> D.andThen (D.succeed << JsObject)
    , D.null JsNull
    ]
Run Code Online (Sandbox Code Playgroud)

Json.Decode.lazy对于JsArrayJsObject构造函数是必需的,因为它们是递归定义的.

这个结构应该处理你抛出的任何东西,并且由你的程序的其余部分来决定如何处理这种灵活的类型.

编辑

正如@Tosh指出的那样,这个解码器可以通过使用map而不是andThen后面的a 来清理succeed:

jsValDecoder : Decoder JsVal
jsValDecoder =
  D.oneOf
    [ D.map JsString D.string
    , D.map JsInt D.int
    , D.map JsFloat D.float
    , D.list (D.lazy (\_ -> jsValDecoder)) |> D.map JsArray
    , D.dict (D.lazy (\_ -> jsValDecoder)) |> D.map JsObject
    , D.null JsNull
    ]
Run Code Online (Sandbox Code Playgroud)

  • 只想评论您可以使用表单:例如`D.map JsString D.string`.至少对我来说有点容易阅读. (2认同)