Haskell中用aeson反序列化表结构JSON遇类型匹配错误求助
问题描述
我是Haskell新手,目前在做一个SQL项目,使用aeson反序列化表结构JSON文件时遇到类型匹配错误,无法解决。以下是相关文件、代码、报错及目标数据类型,请求指导如何编写正确的FromJSON实例。
JSON文件
{ "Table":"flags", "Columns":[ {"Name":"flag", "ColumnType":"StringType"}, {"Name":"value", "ColumnType":"BoolType"}], "Rows":[ [{"Value":"StringValue a"}, {"Value":"BoolValue True"}], [{"Value":"StringValue b"}, {"Value":"BoolValue True"}], [{"Value":"StringValue b"}, {"Value":"NullValue"}], [{"Value":"StringValue b"}, {"Value":"BoolValue False"}] ] }
当前Haskell代码
data FromJSONColumn = FromJSONColumn { deserializedName :: String, deserializedDataType :: String } deriving (Show, Eq, Generic) instance FromJSON FromJSONColumn where parseJSON (Object v) = FromJSONColumn <$> v .: "Name" <*> v .: "ColumnType" parseJSON _ = mzero
报错信息
JSONExample.hs:17:27-32: error: • Couldn't match type ‘[Char]’ with ‘Key’ Expected: Key Actual: String • In the second argument of ‘(.:)’, namely ‘"Name"’ In the second argument of ‘(<$>)’, namely ‘v .: "Name"’ In the first argument of ‘(<*>)’, namely ‘FromJSONColumn <$> v .: "Name"’ | 17 | FromJSONColumn <$> v .: "Name" | ^^^^^^ JSONExample.hs:18:12-23: error: • Couldn't match type ‘[Char]’ with ‘Key’ Expected: Key Actual: String • In the second argument of ‘(.:)’, namely ‘"ColumnType"’ In the second argument of ‘(<*>)’, namely ‘v .: "ColumnType"’ In the expression: FromJSONColumn <$> v .: "Name" <*> v .: "ColumnType" | 18 | <*> v .: "ColumnType" | ^^^^^^^^^^^^
目标数据类型
data ColumnType = IntegerType | StringType | BoolType deriving (Show, Eq) data Column = Column String ColumnType deriving (Show, Eq) data Value = IntegerValue Integer | StringValue String | BoolValue Bool | NullValue deriving (Show, Eq) type Row = [Value] data DataFrame = DataFrame [Column] [Row] deriving (Show, Eq)
解决方案
1. 解决Key类型不匹配问题
报错根源是aeson新版本中,.:操作符的键参数类型为Key而非原生String,有两种解决方式:
方式一:启用OverloadedStrings扩展(推荐)
在代码开头添加扩展声明,让字符串字面量自动转换为Key类型:
{-# LANGUAGE OverloadedStrings #-}
添加后原代码的v .: "Name"即可正常编译。
方式二:手动转换String为Key
若不想启用扩展,可使用fromString函数显式转换:
import Data.Aeson.Key (fromString) parseJSON (Object v) = FromJSONColumn <$> v .: fromString "Name" <*> v .: fromString "ColumnType"
2. 编写目标数据类型的FromJSON实例
无需中间的FromJSONColumn类型,直接为目标类型实现FromJSON:
实现ColumnType的FromJSON
instance FromJSON ColumnType where parseJSON (String t) = case t of "IntegerType" -> pure IntegerType "StringType" -> pure StringType "BoolType" -> pure BoolType _ -> fail $ "未知ColumnType: " ++ show t parseJSON _ = fail "ColumnType必须是字符串类型"
实现Column的FromJSON
instance FromJSON Column where parseJSON (Object v) = Column <$> v .: "Name" <*> v .: "ColumnType" parseJSON _ = fail "Column必须是对象类型"
实现Value的FromJSON
JSON中的Value是嵌套对象,需解析内部字符串格式:
instance FromJSON Value where parseJSON (Object v) = do valueStr <- v .: "Value" parseValueStr valueStr parseJSON _ = fail "Value必须是包含'Value'字段的对象" parseValueStr :: String -> Parser Value parseValueStr "NullValue" = pure NullValue parseValueStr s = case words s of ["StringValue", content] -> pure $ StringValue content ["BoolValue", "True"] -> pure $ BoolValue True ["BoolValue", "False"] -> pure $ BoolValue False ["IntegerValue", numStr] -> case reads numStr of [(num, "")] -> pure $ IntegerValue num _ -> fail $ "IntegerValue格式错误: " ++ numStr _ -> fail $ "Value格式错误: " ++ s
实现DataFrame的FromJSON
instance FromJSON DataFrame where parseJSON (Object v) = DataFrame <$> v .: "Columns" <*> v .: "Rows" parseJSON _ = fail "DataFrame必须是对象类型"
完整可运行代码
{-# LANGUAGE OverloadedStrings #-} import Data.Aeson import Data.Aeson.Types import qualified Data.Text as T data ColumnType = IntegerType | StringType | BoolType deriving (Show, Eq) data Column = Column String ColumnType deriving (Show, Eq) data Value = IntegerValue Integer | StringValue String | BoolValue Bool | NullValue deriving (Show, Eq) type Row = [Value] data DataFrame = DataFrame [Column] [Row] deriving (Show, Eq) instance FromJSON ColumnType where parseJSON (String t) = case t of "IntegerType" -> pure IntegerType "StringType" -> pure StringType "BoolType" -> pure BoolType _ -> fail $ "未知ColumnType: " ++ T.unpack t parseJSON _ = fail "ColumnType必须是字符串类型" instance FromJSON Column where parseJSON (Object v) = Column <$> v .: "Name" <*> v .: "ColumnType" parseJSON _ = fail "Column必须是对象类型" instance FromJSON Value where parseJSON (Object v) = do valueStr <- v .: "Value" parseValueStr (T.unpack valueStr) parseJSON _ = fail "Value必须是包含'Value'字段的对象" parseValueStr :: String -> Parser Value parseValueStr "NullValue" = pure NullValue parseValueStr s = case words s of ["StringValue", content] -> pure $ StringValue content ["BoolValue", "True"] -> pure $ BoolValue True ["BoolValue", "False"] -> pure $ BoolValue False ["IntegerValue", numStr] -> case reads numStr of [(num, "")] -> pure $ IntegerValue num _ -> fail $ "IntegerValue格式错误: " ++ numStr _ -> fail $ "Value格式错误: " ++ s instance FromJSON DataFrame where parseJSON (Object v) = DataFrame <$> v .: "Columns" <*> v .: "Rows" parseJSON _ = fail "DataFrame必须是对象类型"
内容的提问来源于stack exchange,提问作者gailtye
相关产品推荐
相关产品推荐

