简体   繁体   English

榆树解码未知的json结构

[英]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: 特别:

  1. Is it currently possible to handle unknown, possibly deeply nested and heterogeneous json data in Elm? 目前是否可以在Elm中处理未知的,可能深度嵌套的和异构的json数据?
  2. If so, can you give me the key concept or high level intuition on how the author(s) intended data like this to be decoded? 如果是这样,你能否给出关于作者如何将这样的数据解码的关键概念或高级直觉?

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对于JsArrayJsObject构造函数是必需的,因为它们是递归定义的。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM