2016-11-27 3 views
7

Я только начал работать с Elm, чтобы сделать несколько прототипов интерфейса с использованием API-интерфейса Rest, над которым я работаю. В общем, API возвращает «разумные» структуры данных, которые могут быть декодированы, потому что ключи и типы значений хорошо известны, но несколько типов ресурсов возвращают запись data, которая имеет только исходный json, который не имеет заданной структуры.Elm decoding unknown json structure

Все, что я читал до сих пор, похоже, предполагает, что вы знаете структуру данных, которые вы декодируете, тогда как в простых js относительно легко перебирать ключи и отражать типы, чтобы определить, как они должны обрабатываться во время выполнения. Я пока не вижу четкого пути к обработке данных такого рода в Вязе.

Например,

{ 
    "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. В настоящее время можно обрабатывать неизвестные, возможно глубоко вложенные и неоднородные данные json в Elm?
  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 необходимо для JsArray и JsObject конструкторов, поскольку они определяются рекурсивно.

Эта структура должна обрабатывать все, что вы на нее набрасываетесь, и решать остальную часть вашей программы, чтобы решить, что делать с таким гибким типом.

Редактировать

Как @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! Хорошая точка. Единственное моноданное связывание «успех» - хороший запах, который просто так же должен работать с простой «картой». –