将JSON解析成RoseTree(递归解析)
Parsing JSON into RoseTree (recursive parsing)
我正在为将 String
JSON 作为输入和 return RoseTree
结构作为输出的函数而苦苦挣扎。
我的代码如下:
import qualified Data.Aeson as JSON
import qualified Data.Text as T
import qualified Data.Aeson.Types as AT
import Control.Applicative
import qualified Data.ByteString.Char8 as BS
import Data.Attoparsec.ByteString.Char8
data RoseTree a = Empty | RoseTree a [RoseTree a]
deriving (Show)
instance (Show a) => JSON.ToJSON (RoseTree a) where
toJSON (RoseTree n cs) =
JSON.object [T.pack "value" JSON..= show n
, T.pack "children" JSON..= JSON.toJSON cs]
instance (Show a, JSON.FromJSON a) => JSON.FromJSON (RoseTree a) where
parseJSON (JSON.Object o) =
RoseTree <$> o JSON..: T.pack "value"
<*> o JSON..: T.pack "children"
parseRoseTreeFromJSON :: (Show a, JSON.FromJSON a) => String -> (RoseTree a)
parseRoseTreeFromJSON json =
let bs = BS.pack json in case parse JSON.json bs of
(Done rest r) -> case AT.parseMaybe JSON.parseJSON r of
(Just x) -> x
Nothing -> Empty
_ -> Empty
它将 RoseTree
结构转换为 JSON 非常好。比如我运行JSON.encode $ JSON.toJSON $ RoseTree 1 []
,它returns"{\"children\":[],\"value\":\"1\"}"
,运行ningJSON.encode $ JSON.toJSON $ RoseTree 1 [RoseTree 2 []]
returns"{\"children\":[{\"children\":[],\"value\":\"2\"}],\"value\":\"1\"}"
.
但是当我尝试提供 JSON 我已经进入解析器的前一步时,它总是 returns Empty
:
*Main> parseRoseTreeFromJSON "{\"children\":[],\"value\":1}"
Empty
*Main> parseRoseTreeFromJSON "{\"children\":[{\"children\":[],\"value\":\"2\"}],\"value\":\"1\"}"
Empty
同样,
*Main> JSON.decode $ BLS.pack "{\"children\":[{\"children\":[],\"value\":\"2\"}],\"value\":\"1\"}"
Nothing
我怀疑我的解析器定义不正确。但我不能说出到底出了什么问题。我使用正确的方法来处理递归解析吗?递归执行此操作的正确方法是什么?
奇怪的 GHCi 打字规则再次来袭!如果您添加类型注释,一切都会按预期工作:
ghci> :set -XOverloadedStrings
ghci> JSON.decode "{\"children\":[],\"value\":1}" :: Maybe (RoseTree Int)
Just (RoseTree 1 [])
在不知道你试图解析什么类型的情况下(它看到 FromJSON a => Maybe a
),GHCi 默认为 a ~ ()
。您可以通过测试 FromJSON ()
实例并注意到它成功来进行尝试!
ghci> JSON.decode "[]"
Just ()
如果这实际上是为了一个项目(而不仅仅是为了娱乐和学习),请在您的代码中添加一些旁注:
- 我鼓励你查看
OverloadedStrings
(你基本上可以从你的代码中删除几乎所有 T.pack
的使用,因为字符串文字将推断它们是否应该具有类型 String
,lazy/strict Text
, lazy/strict ByteString
等)
- 您可以使用
DeriveGeneric
和 DeriveAnyClass
获得几乎免费的 JSON 解析(尽管我承认行为会 略有 不同从你目前拥有的)
根据这些建议,重写您的代码:
{-# LANGUAGE OverloadedStrings, DeriveGeneric, DeriveAnyClass #-}
import qualified Data.Aeson as JSON
import qualified Data.ByteString.Lazy.Char8 as BS
import GHC.Generics (Generic)
import Data.Maybe (fromMaybe)
data RoseTree a = RoseTree { value :: a, children :: [RoseTree a] }
deriving (Show, Generic, JSON.FromJSON, JSON.ToJSON)
请注意 Data.Aeson
中的 decode
的作用(几乎)与 parseRoseTreeFromJSON
的作用相同...
ghci> :set -XOverloadedStrings
ghci> JSON.encode (RoseTree 1 [])
"{\"children\":[],\"value\":1}"
ghci> JSON.encode (RoseTree 1 [RoseTree 2 []])
"{\"children\":[{\"children\":[],\"value\":2}],\"value\":1}"
ghci> JSON.decode "{\"children\":[],\"value\":1}" :: Maybe (RoseTree Int)
Just (RoseTree {value = 1, children = []})
ghci> JSON.decode "{\"children\":[{\"children\":[],\"value\":2}],\"value\":1}" :: Maybe (RoseTree Int)
Just (RoseTree {value = 1, children = [RoseTree {value = 2, children = []}]})
我正在为将 String
JSON 作为输入和 return RoseTree
结构作为输出的函数而苦苦挣扎。
我的代码如下:
import qualified Data.Aeson as JSON
import qualified Data.Text as T
import qualified Data.Aeson.Types as AT
import Control.Applicative
import qualified Data.ByteString.Char8 as BS
import Data.Attoparsec.ByteString.Char8
data RoseTree a = Empty | RoseTree a [RoseTree a]
deriving (Show)
instance (Show a) => JSON.ToJSON (RoseTree a) where
toJSON (RoseTree n cs) =
JSON.object [T.pack "value" JSON..= show n
, T.pack "children" JSON..= JSON.toJSON cs]
instance (Show a, JSON.FromJSON a) => JSON.FromJSON (RoseTree a) where
parseJSON (JSON.Object o) =
RoseTree <$> o JSON..: T.pack "value"
<*> o JSON..: T.pack "children"
parseRoseTreeFromJSON :: (Show a, JSON.FromJSON a) => String -> (RoseTree a)
parseRoseTreeFromJSON json =
let bs = BS.pack json in case parse JSON.json bs of
(Done rest r) -> case AT.parseMaybe JSON.parseJSON r of
(Just x) -> x
Nothing -> Empty
_ -> Empty
它将 RoseTree
结构转换为 JSON 非常好。比如我运行JSON.encode $ JSON.toJSON $ RoseTree 1 []
,它returns"{\"children\":[],\"value\":\"1\"}"
,运行ningJSON.encode $ JSON.toJSON $ RoseTree 1 [RoseTree 2 []]
returns"{\"children\":[{\"children\":[],\"value\":\"2\"}],\"value\":\"1\"}"
.
但是当我尝试提供 JSON 我已经进入解析器的前一步时,它总是 returns Empty
:
*Main> parseRoseTreeFromJSON "{\"children\":[],\"value\":1}"
Empty
*Main> parseRoseTreeFromJSON "{\"children\":[{\"children\":[],\"value\":\"2\"}],\"value\":\"1\"}"
Empty
同样,
*Main> JSON.decode $ BLS.pack "{\"children\":[{\"children\":[],\"value\":\"2\"}],\"value\":\"1\"}"
Nothing
我怀疑我的解析器定义不正确。但我不能说出到底出了什么问题。我使用正确的方法来处理递归解析吗?递归执行此操作的正确方法是什么?
奇怪的 GHCi 打字规则再次来袭!如果您添加类型注释,一切都会按预期工作:
ghci> :set -XOverloadedStrings
ghci> JSON.decode "{\"children\":[],\"value\":1}" :: Maybe (RoseTree Int)
Just (RoseTree 1 [])
在不知道你试图解析什么类型的情况下(它看到 FromJSON a => Maybe a
),GHCi 默认为 a ~ ()
。您可以通过测试 FromJSON ()
实例并注意到它成功来进行尝试!
ghci> JSON.decode "[]"
Just ()
如果这实际上是为了一个项目(而不仅仅是为了娱乐和学习),请在您的代码中添加一些旁注:
- 我鼓励你查看
OverloadedStrings
(你基本上可以从你的代码中删除几乎所有T.pack
的使用,因为字符串文字将推断它们是否应该具有类型String
,lazy/strictText
, lazy/strictByteString
等) - 您可以使用
DeriveGeneric
和DeriveAnyClass
获得几乎免费的 JSON 解析(尽管我承认行为会 略有 不同从你目前拥有的)
根据这些建议,重写您的代码:
{-# LANGUAGE OverloadedStrings, DeriveGeneric, DeriveAnyClass #-}
import qualified Data.Aeson as JSON
import qualified Data.ByteString.Lazy.Char8 as BS
import GHC.Generics (Generic)
import Data.Maybe (fromMaybe)
data RoseTree a = RoseTree { value :: a, children :: [RoseTree a] }
deriving (Show, Generic, JSON.FromJSON, JSON.ToJSON)
请注意 Data.Aeson
中的 decode
的作用(几乎)与 parseRoseTreeFromJSON
的作用相同...
ghci> :set -XOverloadedStrings
ghci> JSON.encode (RoseTree 1 [])
"{\"children\":[],\"value\":1}"
ghci> JSON.encode (RoseTree 1 [RoseTree 2 []])
"{\"children\":[{\"children\":[],\"value\":2}],\"value\":1}"
ghci> JSON.decode "{\"children\":[],\"value\":1}" :: Maybe (RoseTree Int)
Just (RoseTree {value = 1, children = []})
ghci> JSON.decode "{\"children\":[{\"children\":[],\"value\":2}],\"value\":1}" :: Maybe (RoseTree Int)
Just (RoseTree {value = 1, children = [RoseTree {value = 2, children = []}]})