与Aeson - Haskell的任意JSON密钥

Hon*_*rny 12 json haskell aeson

我有一堆嵌套的JSON对象和任意键.

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

        }
    }

}
Run Code Online (Sandbox Code Playgroud)

其中A,B,C是提前未知.这三个人中的每一个都可能有兄弟姐妹.

我想知道是否有办法以一种优雅的方式将此解析为Aeson的自定义类型.我一直在做的是将它加载到Aeson中Object.

你将如何实现FromJSON这种JSON对象?

谢谢!

编辑:

{
    "USA": {
        "California": {
            "San Francisco": "Some text"
        }
    },
    "Canada": {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

这应该编译到CountryDatabase哪里......

type City            = Map String String
type Country         = Map String City
type CountryDatabase = Map String Country 
Run Code Online (Sandbox Code Playgroud)

Yur*_*ras 18

你可以重用FromJSON实例Map String v.像下一个:

{-# 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)
Run Code Online (Sandbox Code Playgroud)

输出:

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$
Run Code Online (Sandbox Code Playgroud)

PS:你可以不用newtypes 来做,我只是为了清晰起见而使用它们.