稀によく、こういうAPIを見かけます。
{
"name": "json1",
"result_type": "intarray", // result_typeとかいう謎カラムで
"result": [ 1, 2, 3 ] // resultの型が変化する!!!
}
{
"name": "json2",
"result_type": "boolean",
"result": true // 今度はboolだ……
}
{
"name": "json3",
"result_type": "mydata", // mydataとは……?
"result": { // objectだってあるぞ!!
"name": "myname",
"value": "abcdef"
}
}
(死)
こういうのをうまいことマッピングしたいんですけど、どうすればいいんでしょうかね。
型を考える
まずは、各result
に対応する型を定義しちゃいましょう!
data Result
= IntArray (Array Int) -- コンストラクタ名は適当
| BoolValue Boolean
| MyData { name :: String, value :: String }
そして、最終的にほしいデータ型を作ります。
-- 実際にほしいのはこの型
type Response' = { name :: String, result :: Result } -- Resultが直和型だったらresult_typeは不要ですよね!
-- newtypeでラップする必要があるのでこうする
newtype Response = Response Response'
最後に、生Jsonに対応する型にも名前を付けちゃいましょう。
type ResponseJSON =
{ name :: String
, result_type :: String
, result :: Foreign -- resultは`Foreign`型で受ける!
}
準備は整いました!
ReadForeignのインスタンスにしちゃおう
purescript-simple-jsonがプロパティを読むためには、レコードのメンバがReadForeign
のインスタンスであればいいです。ということは、
instance readForeignResponse :: ReadForeign Response where
readImpl o = do
{ name, result, result_type } :: ResponseJSON <- readImpl o
Response <<< { name, result: _ } <$> case result_type of
"intarray" -> pure <<< IntArray =<< readImpl result
"boolean" -> pure <<< BoolValue =<< readImpl result
"mydata" -> pure <<< MyData =<< readImpl result
other -> fail <<< ForeignError $ "cannot read result_type: " <> other
ハイ終わり!!!!
読める……読めるぞ!!
main :: Effect Unit
main = do
case readJSON json of
Left error -> traverse1_ logShow error
Right (Response { name, result }) -> do
logShow name
case result of
IntArray arr -> logShow arr -- [ 1, 2, 3 ]
BoolValue b -> logShow b -- true
MyData d -> logShow d -- { name: "myname", value: "abcdef" }
おわり
- purescript-simple-jsonは神
- jsonで型が一つに定まってなくてもどういうデータが来るのか分かってればマッピングできる
- justin-san(@kimagure)拝み倒していく
- PureScriptめっちゃいい言語