将JSON解析为RoseTree(递归解析)

时间:2017-08-18 16:53:00

标签: json haskell aeson

我正在努力使用String JSON作为输入并返回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 []时,它会返回"{\"children\":[],\"value\":\"1\"}",正在运行JSON.encode $ JSON.toJSON $ RoseTree 1 [RoseTree 2 []]会返回"{\"children\":[{\"children\":[],\"value\":\"2\"}],\"value\":\"1\"}"

但是当我尝试提供JSON时,我已经进入解析器的上一步,它总是返回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

我怀疑我的解析器定义不正确。但我不知道到底出了什么问题。我正在使用正确的方法来处理递归解析吗?以递归方式执行此操作的正确方法是什么?

1 个答案:

答案 0 :(得分:2)

奇怪的GHCi打字规则再次出现!如果添加类型注释,一切都按预期工作:

function palindrome(str) {
    var str_array = str.toLowerCase().split("");
    var no_space = str_array.filter(function(val) {
        return val !== " ";
    });

    // By applying '.slice()', we create a new array
    // reference which can then be reversed and assigned
    // to the 'reverse' variable
    var reverse = no_space.slice().reverse();

    function check(a, b) {
        var partial;
        var result = 1;
        for(var i=0; i < a.length; i++) {
            if(a[i] !== b[i]) {
                // We don't need to keep
                // comparing the two, it
                // already failed
                return 0;
            } else {
                // I've kept this part even though
                // I don't really know what it is
                // intended for
                partial = 1;
                result *= partial;
            }
        }
        return result;
    }
    return check(no_space, reverse) === 1;
}

console.log(palindrome("a b a"));
console.log(palindrome("r y e"));

在不知道您要解析的类型(它看到ghci> :set -XOverloadedStrings ghci> JSON.decode "{\"children\":[],\"value\":1}" :: Maybe (RoseTree Int) Just (RoseTree 1 []) )的情况下,GHCi默认为FromJSON a => Maybe a。你可以通过测试a ~ ()实例并注意到它成功来试试这个!

FromJSON ()

如果这实际上是针对某个项目(并且不仅仅是为了娱乐和学习),还要对您的代码做一些注意事项:

  • 我建议您查看ghci> JSON.decode "[]" Just () (您基本上可以从代码中删除OverloadedStrings的几乎所有用法,因为字符串文字将推断它们是否应该具有类型T.pack,lazy / strict String,lazy / strict Text等。)
  • 你可以使用ByteStringDeriveGeneric几乎免费获得JSON解析(尽管我承认行为略微与你现有的不同)

根据这些建议,这里是对代码的重写:

DeriveAnyClass

请注意{-# 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) 中的decode(几乎)Data.Aeson做了什么......

parseRoseTreeFromJSON