Elm decoding of unknown json structure

I just started working with Elm to make some prototypes of the interface using the Rest API API I'm working on. In general, the API returns “reasonable” data structures that can be decoded, since keys and value types are well known, but several resource types return a data record that has only the original json, which does not have a given structure.

Everything I've read so far seems to suggest that you know the structure of the data you are decoding, whereas in simple js it is relatively easy to iterate over keys and parse types to determine how they should be processed at runtime. I do not yet see a clear path to processing this kind of data in Vyaz.

eg.

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

I would like to know if it is currently possible to parse the value of the data record with something like

 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]) ... 

In particular:

  • Is it currently possible to process unknown, possibly deeply nested and heterogeneous json data in Elm?
  • If so, can you give me a key concept or high-level intuition about how the author suggested that such data should be decoded?
+7
json elm decode
source share
1 answer

Yes, you can write a general purpose decoder. You can first determine the type of union that contains all the possible Json types:

 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 all the features.

 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 required for the JsArray and JsObject because they are defined recursively.

This structure should process everything that you throw on it, and decide the rest of your program to decide what to do with such a flexible type.

Edit

As @Tosh pointed out, this decoder can be cleared using map instead of andThen followed by 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 ] 
+6
source share

All Articles