То, что я пытаюсь сделать, это очень просто .
Я хотел бы преобразовать следующий JSON, который я получаю из внешнего источника:
[{"symbol": "sym1", "description": "desc1"}
{"symbol": "sym1", "description": "desc1"}]
в следующие типы:
data Symbols = Symbols [Symbol]
type Symbol = (String, String)
IВ итоге мы написали следующий код, используя Text.JSON:
instance JSON Symbols where
readJSON (JSArray arr) = either Error (Ok . Symbols) $ resultToEither (f arr [])
where
f ((JSObject obj):vs) acc = either Error (\x -> f vs (x:acc)) $ resultToEither (g (fromJSObject obj) [])
f [] acc = Ok $ reverse acc
f _ acc = Error "Invalid symbol/description list"
g ((name, JSString val):vs) acc = g vs ((name, fromJSString val):acc)
g [] acc = valg acc
g _ acc = Error "Invalid symbol/description record"
valg xs = case (sym, desc) of
(Nothing, _) -> Error "Record is missing symbol"
(_, Nothing) -> Error "Record is missing description"
(Just sym', Just desc') -> Ok (sym', desc')
where
sym = lookup "symbol" xs
desc = lookup "description" xs
showJSON (Symbols syms) = JSArray $ map f syms
where
f (sym, desc) = JSObject $ toJSObject [("symbol", JSString $ toJSString sym),
("description", JSString $ toJSString desc)]
Это стало самым неуклюжимым Haskell, который я когда-либо писал.readJSON
просто не выглядит правильно.Конечно, showJSON
существенно короче, но что случилось с этими JSString $ toJSString
и JSObject $ toJSObject
вещами, которые я вынужден вставить сюда?И resultToEither
?
Я использую Text.JSON неправильно?Есть ли лучший способ?
Хорошо, это больше похоже на это.Я получил readJSON
до следующего благодаря разъяснениям и идеям Романа и Грейзера.В каждой точке он обнаружит неправильно отформатированный JSON и выведет ошибку, а не выдаст исключение.
instance JSON Symbols where
readJSON o = fmap Symbols (readJSON o >>= mapM f)
where
f (JSObject o) = (,) <$> valFromObj "symbol" o <*> valFromObj "description" o
f _ = Error "Unable to read object"