在 json 解析中使用文件名 (Haskell Aeson)
Using file name in json parsing (Haskell Aeson)
我没有Haskell方面的经验。我正在尝试使用 aeson 将许多 .json
文件解析为 Haskell 中的数据结构。但是,由于我无法控制的原因,我需要将解析数据的文件的名称存储为我的数据中的字段之一。到目前为止,我所拥有的一个简单示例是:
data Observation = Observation { id :: Integer
, value :: Integer
, filename :: String}
instance FromJSON Observation where
parseJson (Object v) =
Observation <$> (read <$> v .: "id")
<*> v .: "value"
<*> ????
我的问题是:在解析具有文件名访问权限的 json 文件时,能够序列化我的数据的聪明方法是什么?
我想到的是定义另一个 data
,例如 NotNamedObservation
,对其进行初始化,然后使用一个函数来转换 NotNamedObservation -> String -> Observation
(其中 String 是文件名),但这听起来就像一个非常糟糕的方法。
谢谢。
当您不控制 data
定义并且对要解析的格式有严格要求时,最好显式编写(反)序列化器。
如果需要外部信息来完全构造值,请避免使用 FromJSON
/ToJSON
类型 类,只需编写独立的解析器。
aeson 的派生机制更适合与自己对话的应用程序(因此只关心 parseJSON
和 toJSON
之间的往返),或者可以灵活定义 JSON 格式或 Haskell 类型。
如果出于某种原因您仍然必须使用这些 类,当然,一种选择是将 undefined
放在那些缺失的字段中。要更多地依赖类型系统,您还可以通过 "phase" 参数化类型(假设您可以再次调整数据类型),这是一个包装一些字段的类型构造函数。
- Functor functors
- 保持该样式与常规记录兼容的一种方法是使用 this HKD/defunctionalization design pattern.
data Observation' p = Observation
{ id :: Integer
, value :: Integer
, filename :: p String }
-- This is isomorphic to the original Observation data type
type Observation = Observation Identity
-- When we don't have the filename available, we keep the field empty with Proxy
instance FromJSON (Observation' Proxy) where
...
mkObservation :: FileName -> Observation' Proxy -> Observation
只需让您的实例成为从文件路径到观察的函数:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson
import qualified Data.ByteString.Lazy as LBS
import System.Environment
data Observation = Observation { ident :: Integer
, value :: Integer
, filename :: FilePath
} deriving (Show)
instance FromJSON (FilePath -> Observation) where
parseJSON (Object v) =
do i <- read <$> v .: "id"
l <- v .: "value"
pure $ Observation i l
main :: IO ()
main = do
files <- getArgs
fileContents <- traverse LBS.readFile files
print fileContents
let fs = map (maybe (error "Invalid json") id . decode) fileContents
jsons :: [Observation]
jsons = zipWith ($) fs files
print jsons
我没有Haskell方面的经验。我正在尝试使用 aeson 将许多 .json
文件解析为 Haskell 中的数据结构。但是,由于我无法控制的原因,我需要将解析数据的文件的名称存储为我的数据中的字段之一。到目前为止,我所拥有的一个简单示例是:
data Observation = Observation { id :: Integer
, value :: Integer
, filename :: String}
instance FromJSON Observation where
parseJson (Object v) =
Observation <$> (read <$> v .: "id")
<*> v .: "value"
<*> ????
我的问题是:在解析具有文件名访问权限的 json 文件时,能够序列化我的数据的聪明方法是什么?
我想到的是定义另一个 data
,例如 NotNamedObservation
,对其进行初始化,然后使用一个函数来转换 NotNamedObservation -> String -> Observation
(其中 String 是文件名),但这听起来就像一个非常糟糕的方法。
谢谢。
当您不控制 data
定义并且对要解析的格式有严格要求时,最好显式编写(反)序列化器。
如果需要外部信息来完全构造值,请避免使用 FromJSON
/ToJSON
类型 类,只需编写独立的解析器。
aeson 的派生机制更适合与自己对话的应用程序(因此只关心 parseJSON
和 toJSON
之间的往返),或者可以灵活定义 JSON 格式或 Haskell 类型。
如果出于某种原因您仍然必须使用这些 类,当然,一种选择是将 undefined
放在那些缺失的字段中。要更多地依赖类型系统,您还可以通过 "phase" 参数化类型(假设您可以再次调整数据类型),这是一个包装一些字段的类型构造函数。
- Functor functors
- 保持该样式与常规记录兼容的一种方法是使用 this HKD/defunctionalization design pattern.
data Observation' p = Observation
{ id :: Integer
, value :: Integer
, filename :: p String }
-- This is isomorphic to the original Observation data type
type Observation = Observation Identity
-- When we don't have the filename available, we keep the field empty with Proxy
instance FromJSON (Observation' Proxy) where
...
mkObservation :: FileName -> Observation' Proxy -> Observation
只需让您的实例成为从文件路径到观察的函数:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson
import qualified Data.ByteString.Lazy as LBS
import System.Environment
data Observation = Observation { ident :: Integer
, value :: Integer
, filename :: FilePath
} deriving (Show)
instance FromJSON (FilePath -> Observation) where
parseJSON (Object v) =
do i <- read <$> v .: "id"
l <- v .: "value"
pure $ Observation i l
main :: IO ()
main = do
files <- getArgs
fileContents <- traverse LBS.readFile files
print fileContents
let fs = map (maybe (error "Invalid json") id . decode) fileContents
jsons :: [Observation]
jsons = zipWith ($) fs files
print jsons