2017-01-04 1 views
3

Я хотел бы декодировать ответ API, в котором одно из значений полей (category) определит, как декодировать другое поле (configuration) с использованием разных поддекодеров ,Возможно ли условное декодирование определенных полей с использованием elm-decode-трубопровода

Я был в состоянии выполнить такую ​​вещь, используя Json.Decode.mapn функции и функции andThen, но мне было интересно, если есть способ сделать такую ​​вещь, используя elm-decode-pipeline как он имеет более приятный интерфейс API, и я буду бежать из mapn функций в конечном счете.

minimmum и несколько тривиальный пример будет выглядеть так:

type alias Machine = 
    { name : String 
    , specs : MachineSpecs 
    } 

type MachineSpecs 
    = ElectricMachine ElectricSpecs 
    | MechanicalMachine MechanicalSpecs 
    | UnknownMachine 

type alias ElectricSpecs = 
    { voltage : Int 
    } 

type alias MechanicalSpecs = 
    { gears : Int 
    } 

И некоторые действительные ответы JSON бы эти формы:

{ 
    "name": "Foo electric machine", 
    "category": "electric", 
    "configuration": { 
    "voltage": 12 
    } 
} 
{ 
    "name": "Bar mechanical machine", 
    "category": "mechanical", 
    "configuration": { 
    "gears": 5 
    } 
} 
{ 
    "name": "Some machine of unknown category", 
    "category": "foo" 
} 

Я попробовал подобный подход к той, которую я использовал с функциями mapn, но это не сработает.

decoder : Decoder Machine 
decoder = 
    decode Machine 
     |> required "name" string 
     |> required "category" (string |> andThen catDec) 


catDec : String -> Decoder MachineSpecs 
catDec cat = 
    case cat of 
     "electric" -> 
      map ElectricMachine electricDecoder 

     "mechanical" -> 
      map MechanicalMachine mechanicalDecoder 

     _ -> 
      succeed UnknownMachine 


electricDecoder : Decoder ElectricSpecs 
electricDecoder = 
    decode ElectricSpecs 
     |> requiredAt [ "configuration", "voltage" ] int 


mechanicalDecoder : Decoder MechanicalSpecs 
mechanicalDecoder = 
    decode MechanicalSpecs 
     |> requiredAt [ "configuration", "gears" ] int 

На самом деле, я не видел ни одного примера, в Интернете или документы, используя как Json.Decode.Pipeline и andThen в то же время, так что я не уверен, если это вообще возможно.

Я настроил онлайн пример этого вопроса, показывая, как он не может декодировать условную часть: https://runelm.io/c/3ut

ответ

4

В качестве альтернативы, вы можете разместить свои andThen привязок до трубопровода (ellie example):

decoder : Decoder Machine 
decoder = 
    field "category" string 
     |> andThen catDec 
     |> andThen 
      (\cat -> 
       decode Machine 
        |> required "name" string 
        |> hardcoded cat 
      ) 

Если у вас заканчиваются номера mapN, подумайте о переходе на andMap (или инфикс версии |:) в пакет elm-community/json-extra.

4

Chad Gilbert's answer просто работает (спасибо!), И это заставляет меня читать исходный код Json.Decode.Pipeline, чтобы понять немного больше о том, как был реализован трубопровод, и я нашел альтернативное решение, которое немного более кратким, поэтому я подумал о размещении его здесь:

decoder : Decoder Machine 
decoder = 
    decode Machine 
     |> required "name" string 
     |> custom (field "category" string |> andThen catDec) 
Смежные вопросы