Lo que estoy tratando de hacer es realmente simple.¿Mi uso de Haskell's Text.JSON se considera feo?
me gustaría convertir el siguiente JSON, que estoy recibiendo de una fuente externa:
[{"symbol": "sym1", "description": "desc1"}
{"symbol": "sym1", "description": "desc1"}]
en los siguientes tipos:
data Symbols = Symbols [Symbol]
type Symbol = (String, String)
terminé escribiendo el siguiente código usando 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)]
Esto tiene para el Haskell más poco elegante que he escrito. readJSON
simplemente no se ve bien. Claro, showJSON
es sustancialmente más corto, pero ¿qué pasa con este JSString $ toJSString
y JSObject $ toJSObject
cosas que estoy obligado a poner aquí? ¿Y resultToEither
?
¿Estoy usando Text.JSON incorrecto? ¿Hay una mejor manera?
Bueno, esto es más como él. Obtuve readJSON
de la siguiente manera gracias a las aclaraciones e ideas de Roman y Grazer. En cada punto detectará un JSON formateado incorrectamente y generará un error en lugar de lanzar una excepción.
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"
Puedo cambiar el título, pero no creo que cambie la pregunta. ¿Cómo usaría mapM aquí donde no hay mónadas involucradas para simplificar? – qrest
'Result' * es * una mónada (http://hackage.haskell.org/packages/archive/json/0.4.4/doc/html/Text-JSON.html#t:Result). Ahora actualizaré mi respuesta con un código de muestra. Tenga en cuenta que descarté algunos mensajes de error; puede restaurarlos si lo desea. –
Para mejorar el manejo de errores (el código que mostré puede arrojar excepciones) escriba versiones seguras de funciones de coincidencia de patrones (para JSObject y JSString) que devolverá Error en la falla de coincidencia de patrones y los usará monádicamente. –