我刚刚开始与Elm合作,使用我正在研究的Rest API进行一些前端原型设计。通常,API返回可以解码的“合理”数据结构,因为键和值类型是众所周知的,但是几种资源类型返回一个data
条目,它只有原始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])
...
具体做法是:
答案 0 :(得分:9)
是的,可以编写通用解码器。您可以先定义一个包含所有可能的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
]
JsArray
和JsObject
构造函数需要 Json.Decode.lazy
,因为它们是递归定义的。
这个结构应该处理你抛出的任何东西,并且由你的程序的其余部分来决定如何使用这种灵活的类型。
修改强>
正如@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
]
答案 1 :(得分:1)
在Chad's的出色答案中,缺少布尔类型。这是一个能够处理布尔值的完整模块:
module Data.JsonValue exposing (JsonValue(..), decoder)
import Dict exposing (Dict)
import Json.Decode as Decode
exposing
( Decoder
, dict
, string
, int
, float
, list
, null
, oneOf
, lazy
, map
, bool
)
type JsonValue
= JsonString String
| JsonInt Int
| JsonFloat Float
| JsonBoolean Bool
| JsonArray (List JsonValue)
| JsonObject (Dict String JsonValue)
| JsonNull
decoder : Decoder JsonValue
decoder =
oneOf
[ map JsonString string
, map JsonInt int
, map JsonFloat float
, map JsonBoolean bool
, list (lazy (\_ -> decoder)) |> map JsonArray
, dict (lazy (\_ -> decoder)) |> map JsonObject
, null JsonNull
]