一个例子更容易解释,所以我想用木薯将此数据解析为数据类型
title;authors
Cambridge Economic History;Ian MorrisWalter,ScheidelRichard,P Saller
我尝试执行以下操作,但它不起作用(最小的不起作用示例):
{-# LANGUAGE OverloadedStrings, DeriveGeneric #-}
module Library where
import Data.Csv
import Data.List.Split
import qualified Data.Vector as V
import qualified Data.Text as T
import GHC.Generics
data Book = Book {
title :: T.Text,
authors :: Authors
} deriving (Generic, Show)
type Authors = [T.Text]
instance FromNamedRecord Book
instance FromNamedRecord Authors
parseField "authors" =
pure $ splitOn "," ???
opts = defaultDecodeOptions {
decDelimiter = fromIntegral (ord ';')
}
main c = do
csvData <- BL.readFile "data.csv"
let res = decodeByNameWith opts csvData :: Either String (Header, V.Vector Book)
可以用木薯来做吗?谢谢!
最佳答案
最简单的方法是编写自定义的 FromNamedRecord
Book
的实例,而不是派生 Generic
一。它看起来像:
instance FromNamedRecord Book where
parseNamedRecord m = Book <$>
m .: "title" <*>
(T.splitOn "," <$> m .: "authors")
在这里,m .: "authors"
将作者字段检索为 Text
记录,以及 T.splitOn ","
对该结果进行 fmapped ( <$>
) 以分割 Text
进入[Text]
以逗号分隔。
完整的程序:
{-# LANGUAGE OverloadedStrings #-}
module Library where
import Data.Char
import Data.Csv
import qualified Data.Vector as V
import qualified Data.Text as T
import Data.ByteString.Lazy as BL
data Book = Book {
title :: T.Text,
authors :: Authors
} deriving (Show)
type Authors = [T.Text]
instance FromNamedRecord Book where
parseNamedRecord m = Book <$>
m .: "title" <*>
(T.splitOn "," <$> m .: "authors")
opts = defaultDecodeOptions {
decDelimiter = fromIntegral (ord ';')
}
main = do
csvData <- BL.readFile "data.csv"
let res = decodeByNameWith opts csvData :: Either String (Header, V.Vector Book)
print res
给予:
λ> main
Right (["title","authors"],[Book {title = "Cambridge Economic History",
authors = ["Ian MorrisWalter","ScheidelRichard","P Saller"]}])
请注意,这不允许您处理作者列表中每个作者的引用,因此如果您需要使用嵌入的逗号来解析作者,例如:
Another Book;John Smith,"Anne Douglas, Jr."
你会不走运的。 Cassava 将拒绝解析像这样嵌入引号的“作者”字段,我认为您最终将不得不编写自己的专用 CSV 解析器。
关于csv - 使用 Cassava 读取 CSV 列中的嵌套列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67209738/