If I got a YAML,
canary_instance:
name: instance_name
metadata:
a:
b:
c: d
How to easily get value of nested block without too much boilerplate code? e.g. like in Python obj["canary_instance"]["metadata"]["a"]["b"]["c"]
without having to write new data type for each key
I have followed this tutorial, and modified to fit the above example
{-# LANGUAGE OverloadedStrings #-}
module Parser where
import Prelude hiding (readFile)
import Data.Aeson
import Data.Map (Map)
import qualified Data.Map as Map
import qualified Data.Yaml as Y
data Instance = Instance
{ name :: String
, metadata :: InstanceMetadata
} deriving Show
data InstanceMetadata = InstanceMetadata
{ a :: InstanceMetadataA
} deriving Show
data InstanceMetadataA = InstanceMetadataA
{ b :: InstanceMetadataB
} deriving Show
data InstanceMetadataB = InstanceMetadataB
{ c :: String
} deriving Show
instance FromJSON Instance where
parseJSON (Object m) = Instance <$>
m .: "name" <*>
m .: "metadata"
parseJSON x = fail ("not an object: " show x)
instance FromJSON InstanceMetadata where
parseJSON (Object m) = InstanceMetadata <$>
m .: "a"
parseJSON x = fail ("not an object: " show x)
instance FromJSON InstanceMetadataA where
parseJSON (Object m) = InstanceMetadataA <$>
m .: "b"
parseJSON x = fail ("not an object: " show x)
instance FromJSON InstanceMetadataB where
parseJSON (Object m) = InstanceMetadataB <$>
m .: "c"
parseJSON x = fail ("not an object: " show x)
main :: IO ()
main = do
y <- either (error . show) id <$>
Y.decodeFileEither "example.yaml" :: IO (Map String Instance)
print y
print $ fmap (c . b . a . metadata) (Map.lookup "canary_instance" y)
The output is
> :main
fromList [("canary_instance",Instance {name = "instance_name", metadata = InstanceMetadata {a = InstanceMetadataA {b = InstanceMetadataB {c = "d"}}}})]
Just "d"
CodePudding user response:
One way of doing this is to use the lens-aeson
package. Here's how you'd modify your program to do that:
{-# LANGUAGE OverloadedStrings #-}
module Parser where
import Prelude hiding (readFile)
import Control.Lens
import Data.Aeson
import Data.Aeson.Lens
import qualified Data.Yaml as Y
main :: IO ()
main = do
y <- either (error . show) id <$>
Y.decodeFileEither "example.yaml" :: IO Value
print y
print $ y ^? key "canary_instance" . key "metadata" . key "a" . key "b" . key "c" . _String