2010-09-10 39 views
5

我想要做的是真的很簡單我使用Haskell的Text.JSON被認爲是醜陋的?

我想轉換以下JSON,我正在從外部源獲取:

[{"symbol": "sym1", "description": "desc1"} 
{"symbol": "sym1", "description": "desc1"}] 

爲以下幾種類型:

data Symbols = Symbols [Symbol] 
type Symbol = (String, String) 

我最後寫了下面的代碼使用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)] 

這已經到了我所寫過的最不雅的哈斯克爾。 readJSON只是看起來不對。當然,showJSON大大縮短了,但是這個JSString $ toJSStringJSObject $ 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" 

回答

6

請問您可否將標題更改爲更精確的標題?從「Haskell的Text.JSON被認爲醜陋...」到像「我的代碼使用Text.JSON被認爲醜陋...」

你的一半代碼包含顯式遞歸 - 你爲什麼需要它?從快速看起來像mapM應該就足夠了。

更新:示例代碼

instance JSON Symbols where 
    readJSON (JSArray arr) = fmap Symbols (f arr) 
    f = mapM (\(JSObject obj) -> g . fromJSObject $ obj) 
    g = valg . map (\(name, JSString val) -> (name, fromJSString val)) 

    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 
+0

我可以改變標題,但我不認爲它改變了問題。在這裏沒有涉及monad簡化的地方,你會如何使用mapM? – qrest 2010-09-10 06:27:29

+0

'Result' *是* monad(http://hackage.haskell.org/packages/archive/json/0.4.4/doc/html/Text-JSON.html#t:Result)。我現在將用示例代碼更新我的答案。請注意,我放棄了一些錯誤消息 - 如果需要,您可以恢復它們。 – 2010-09-10 06:54:02

+0

爲了改善錯誤處理(我展示的代碼可能會拋出異常),編寫模式匹配函數的安全版本(用於JSObject和JSString),它將在模式匹配失敗時返回錯誤並單點使用它們。 – 2010-09-10 07:02:07

2

重新排列,從羅馬的很好的解決方案了一點。我認爲這可能會更具可讀性。

instance JSON Symbols where 
    readJSON o = fmap Symbols (readJSON o >>= mapM f) 
    where 
     f (JSObject o) = let l = fromJSObject o 
         in do s <- jslookup "symbol" l 
          d <- jslookup "description" l 
          return (s,d) 
     f _ = Error "Expected an Object" 
     jslookup k l = maybe (Error $ "missing key : "++k) readJSON (lookup k l) 
+0

這個解決方案的一部分內容是我在試圖獲得關於toJSObject,toJSString,古怪。例如,jslookup演示了readJSON可以有效地從JSValue中提取字符串,而不必經歷所有這些。 – qrest 2010-09-10 14:52:21

+0

如果存在JSObject存儲的JSON實例,則可能不需要'fromJSObject'。實際上,如果使用MAP_AS_DICT編譯json包,那麼它應該像下面這樣簡單:readJSON :: Result [Map String String]。然後進行一些簡單的調整,把地圖變成你想要的結構 – 2010-09-11 01:49:28