Aeson中的任意JSON键 - Haskell

12

我有一堆嵌套的JSON对象,其中包含任意键。

{
    "A": {
        "B": {
            "C": "hello"

        }
    }

}

其中ABC是事先未知的。这三个也可能有兄弟姐妹。

我想知道是否有一种优雅的方式,在Aeson中将它解析为自定义类型。我一直在做的是将其加载到一个Aeson Object中。

您会如何实现此类JSON对象的FromJSON

谢谢!

编辑:

{
    "USA": {
        "California": {
            "San Francisco": "Some text"
        }
    },
    "Canada": {
        ...
    }
}

这应该编译成CountryDatabase,其中...

type City            = Map String String
type Country         = Map String City
type CountryDatabase = Map String Country 

1
你真正想要如何解析这个JSON并不是很清楚。它是否总是只有3个嵌套键和一个字符串? - Matvey Aksenov
你能举个例子,说明你想解析的自定义类型吗?我认为这会澄清问题。 - Mike Craig
问题已更新,提供了更具体的数据结构示例。 - Honza Pokorny
1个回答

18

您可以重复使用Map String vFromJSON实例。类似下面这样:

{-# LANGUAGE OverloadedStrings #-}

import Data.Functor
import Data.Monoid
import Data.Aeson
import Data.Map (Map)
import qualified Data.ByteString.Lazy as LBS
import System.Environment

newtype City = City (Map String String)
  deriving Show

instance FromJSON City where
  parseJSON val = City <$> parseJSON val

newtype Country = Country (Map String City)
  deriving Show

instance FromJSON Country where
  parseJSON val = Country <$> parseJSON val

newtype DB = DB (Map String Country)
  deriving Show

instance FromJSON DB where
  parseJSON val = DB <$> parseJSON val

main :: IO ()
main = do
  file <- head <$> getArgs
  str <- LBS.readFile file
  print (decode str :: Maybe DB)

输出结果:

shum@shum-lt:/tmp/shum$ cat in.js 
{
    "A": {
        "A1": {
            "A11": "1111",
            "A22": "2222"
        }
    },
    "B": {
    }
}
shum@shum-lt:/tmp/shum$ runhaskell test.hs in.js 
Just (DB (fromList [("A",Country (fromList [("A1",City (fromList [("A11","1111"),("A22","2222")]))])),("B",Country (fromList []))]))
shum@shum-lt:/tmp/shum$

备注:您可以在不使用newtype的情况下完成这个任务,我只是为了更清晰地表达而使用了它们。


这个答案非常有帮助!能否修改它以忽略非字符串值?(例如,用1111替换"1111"会导致解析失败。) - davidchambers
这个实例的 ToJSON 实现会是什么样子? - AdHominem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接