[英]Elm decoding unknown json structure
I've just started working with Elm to do some front-end prototyping using a Rest API I'm working on. 我刚刚开始与Elm合作,使用我正在研究的Rest API进行一些前端原型设计。 In general, the API returns "reasonable" data structures that can be decoded because the keys and value-types are well-known, but several resource types return a
data
entry that just has raw json that has no predetermined structure. 通常,API返回可以解码的“合理”数据结构,因为键和值类型是众所周知的,但是几种资源类型返回的
data
条目只有没有预定结构的原始json。
Everything I've read so far seems to assume you know the structure of the data you're decoding, whereas in plain js it's relatively easy to loop over the keys and reflect on the types in order to determine how they should be handled at runtime. 到目前为止我读过的所有东西似乎都假设你知道你正在解码的数据的结构,而在普通的js中,相对容易循环键并反映类型以确定它们应该如何在运行时处理。 I'm not seeing a clear path toward handling this kind of data in Elm yet.
我还没有看到在Elm中处理这类数据的明确路径。
Eg, 例如,
{
"name":"foo",
"data": {
"bar": [{"baz":123}, "quux"]
},
...
}
I'd like to know if it is currently possible to parse the value of the data
entry with something akin to 我想知道目前是否可以用类似的东西来解析
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])
...
Specifically: 特别:
Yes, it is possible to write a general purpose decoder. 是的,可以编写通用解码器。 You can first define a union type that holds all possible Json types:
您可以先定义一个包含所有可能的Json类型的联合类型:
type JsVal
= JsString String
| JsInt Int
| JsFloat Float
| JsArray (List JsVal)
| JsObject (Dict String JsVal)
| JsNull
And now you can use Json.Decode.oneOf
to try every possibility. 现在你可以使用
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
is necessary for the JsArray
and JsObject
constructors because they are defined recursively. Json.Decode.lazy
对于JsArray
和JsObject
构造函数是必需的,因为它们是递归定义的。
This structure should handle anything you throw at it, and it will be up to the rest of your program to decide what to do with such a flexible type. 这个结构应该处理你抛出的任何东西,并且由你的程序的其余部分来决定如何处理这种灵活的类型。
Edit 编辑
As @Tosh pointed out, this decoder can be cleaned up by using map
instead of an andThen
followed by a succeed
: 作为@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
]
In Chad's excellent answer, the boolean type is missing. 在Chad的优秀答案中,缺少布尔类型。 Here's a full module able to handle booleans as well:
这是一个完整的模块,能够处理布尔值:
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
]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.