2017-07-18 67 views
2

我需要解碼一個JSON數組,其頭部項目的類型爲User,所有尾部項目爲Nickname's。數組長度事先不知道,我不能更改JSON表示。elm解碼非均勻陣列

JSON樣本:

{ "userdata" : [ 
    { 
     "id" : 1, 
     "name" : "MyName", 
     "email" : "[email protected]" 
    }, 
    { 
     "name" : "n1" 
    }, 
    { 
     "name" : "n2" 
    } 
] 
} 

我的類型定義:

module Decoders exposing (..) 
type alias User = 
    { id : Int 
    , name : String 
    , email : String 
    } 

type alias Nickname = 
    { name : String 
    } 

type alias Model = 
    { user : User 
    , nicknames : List Nickname 
    } 

有在UserNickname其他許多不同的領域,但我在這裏縮短它保持示例簡單。

解碼器:

decodeUser : Json.Decode.Decoder User 
decodeUser = 
    Json.Decode.Pipeline.decode User 
     |> Json.Decode.Pipeline.required "id" (Json.Decode.int) 
     |> Json.Decode.Pipeline.required "name" (Json.Decode.string) 
     |> Json.Decode.Pipeline.required "email" (Json.Decode.string) 


decodeNickname : Json.Decode.Decoder Nickname 
decodeNickname = 
    Json.Decode.Pipeline.decode Nickname 
     |> Json.Decode.Pipeline.required "name" (Json.Decode.string) 


decodeModel : Json.Decode.Decoder Model 
decodeModel = 
    Json.Decode.Pipeline.decode Model 
     |> Json.Decode.Pipeline.required "userdata" (Json.Decode.index 0 decodeUser) 
     |> Json.Decode.Pipeline.hardcoded [ Nickname "Nick", Nickname "Names" ] 

測試:

decodesModel : Test 
decodesModel = 
    test "Decodes a user and list of nicknames" <| 
     \() -> 
      let 
       input = 
        """ 
         { "userdata" : [ 
         { 
          "id" : 1, 
          "name" : "MyName", 
          "email" : "[email protected]" 
         }, 
         { 
          "name" : "n1" 
         }, 
         { 
          "name" : "n2" 
         } 
         ] 
         } 
        """ 

       decodedOutput = 
        Json.Decode.decodeString 
         Decoders.decodeModel 
         input 

       nicknames = 
        [ Decoders.Nickname "n1", Decoders.Nickname "n2" ] 

       user = 
        Decoders.User 1 "MyName" "[email protected]" 

       expectation = 
        Decoders.Model user nicknames 
      in 
       Expect.equal decodedOutput 
        (Ok expectation) 

因爲我剛纔硬編碼的Nickname deserialisation測試失敗:

✗ Decodes a user and list of nicknames 

Ok { user = { id = 1, name = "MyName", email = "[email protected]" }, nicknames = [{ name = "n1" },{ name = "n2" }] } 
╷ 
│ Expect.equal 
╵ 
Ok { user = { id = 1, name = "MyName", email = "[email protected]" }, nicknames = [{ name = "Nick" },{ name = "Names" }] } 

什麼是下降頭的最佳途徑項並將數組的其餘部分反序列化爲列表Nickname的?

回答

1

我會通過首先製作一個更高階的解碼器來解決這個問題,它需要兩個解碼器作爲輸入:第一個解碼器解碼列表的頭部,第二個解碼器解碼尾部。簽名可能是這樣的:

headAndTailDecoder : Decoder a -> Decoder b -> Decoder (a, List b) 

下面是我在實現這個功能的初步嘗試。這是一個有點冗長,因爲它首先列表解碼成的Json.Decode.Value項目的清單,然後運行所產生的名單上的解碼器:

import Json.Decode as JD exposing (Decoder) 
import Result.Extra exposing (combine) 

headAndTailDecoder : Decoder a -> Decoder b -> Decoder (a, List b) 
headAndTailDecoder head tail = 
    JD.list JD.value 
     |> JD.andThen 
      (\values -> 
       case values of 
        [] -> 
         JD.fail "Empty list" 

        h :: t -> 
         case (JD.decodeValue head h, List.map (JD.decodeValue tail) t |> combine) of 
          (Ok headDecoded, Ok tailDecoded) -> 
           JD.succeed (headDecoded, tailDecoded) 

          _ -> 
           JD.fail "Invalid" 
      ) 

這或許可以得到優化,但它能夠完成任務。針對您的輸入運行收益率:

JD.field "userdata" (headAndTailDecoder decodeUser decodeNickname) 
    |> JD.map (\(h, t) -> Model h t) 

-- yields: Ok { user = { id = 1, name = "MyName", email = "[email protected]" }, nicknames = [{ name = "n1" },{ name = "n2" }] } 
+0

感謝,作品像一個魅力!很好的策略來解碼成模式匹配的Json.Decode.Value項目。 – Giraffen