You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 11:48:26