2016-11-27 47 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!好點,一個單一的「成功」一元綁定是一個很好的味道,一個簡單的「地圖」應該工作。 –