【问题标题】:Using file name in json parsing (Haskell Aeson)在 json 解析中使用文件名 (Haskell Aeson)
【发布时间】:2018-04-21 01:59:20
【问题描述】:

我没有使用 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 文件时,能够序列化我的数据的智能方法是什么?

我想到的是定义另一个dataNotNamedObservation,初始化它,然后有一个转换NotNamedObservation -&gt; String -&gt; Observation(其中String 是文件名)的函数,但这听起来像一个非常糟糕的方法。

谢谢。

【问题讨论】:

  • 我认为您定义一个表示“没有文件名的观察”的额外数据类型的想法非常好!
  • 它可能看起来不太聪明,但我也认为你建议的解决方案是最好的。特别是如果它有签名NotNamedObservation -&gt; FilePath -&gt; Observation,那么很明显发生了什么。

标签: haskell aeson


【解决方案1】:

只需让您的实例成为从文件路径到 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

【讨论】:

    【解决方案2】:

    当您不控制data 定义并且您对解析格式有严格要求时,最好显式编写(反)序列化程序。

    如果需要外部信息来完全构造值,请避免使用FromJSON/ToJSON 类型类,而只需编写独立的解析器。

    aeson 的派生机制更适合于自言自语的应用程序(因此只关心parseJSONtoJSON 之间的往返),或者具有灵活性的应用程序定义 JSON 格式或 Haskell 类型。


    如果由于某种原因您仍然必须使用这些类,一种选择当然是将undefined 放在那些缺失的字段中。要更多地依赖类型系统,您还可以通过“阶段”参数化类型(再次假设您可以调整数据类型),这是一个包装一些字段的类型构造函数。


    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
    

    【讨论】:

    • 对于有更多haskell经验的人来说,这将是一个强有力的答案,但我认为你在这里把消防水带变成了新手。不过,Sandy Maguire 的文章很棒,非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多