2016-11-27 39 views
7

我刚刚开始与Elm合作,使用我正在开发的Rest API进行一些前端原型设计。一般来说,API返回可以解码的“合理”数据结构,因为密钥和值类型是众所周知的,但是一些资源类型返回只有原始json没有预定结构的data条目。Elm解码未知的json结构

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

例如,

{ 
    "name":"foo", 
    "data": { 
    "bar": [{"baz":123}, "quux"] 
    }, 
    ... 
} 

我想知道,如果它是目前可能的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]) 
     ... 

具体做法是:

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

恐怕不是“Elm-比如“接收你不认识的结构。在Elm中,你总是期望一个对象包含某些属性,而且Elm甚至会在运行时检查该对象是否存在所期望的所有属性。 –

回答

6

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

type JsVal 
    = JsString String 
    | JsInt Int 
    | JsFloat Float 
    | JsArray (List JsVal) 
    | JsObject (Dict String JsVal) 
    | JsNull 

现在你可以使用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 
    ] 

Json.Decode.lazy因为它们被递归地定义是必要的JsArrayJsObject构造函数。

这个结构应该处理任何事情,它将由您的程序的其余部分决定如何处理这种灵活类型。

编辑

由于@Tosh指出,该解码器可以通过使用map而不是andThen后跟一个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 
    ] 
+2

只想评论一下你可以使用一个表单:例如'D.map JsString D.string'。至少,对我来说,读起来要容易一点。 – Tosh

+0

谢谢,@Tosh!好点,一个单一的“成功”一元绑定是一个很好的味道,一个简单的“地图”应该工作。 –