Haskell 在同一字段中使用两种不同的可能类型派生Aeson类型类
我有一个API,它以以下形式返回JSON结果:Haskell 在同一字段中使用两种不同的可能类型派生Aeson类型类,haskell,type-parameter,aeson,to-json,fromjson,Haskell,Type Parameter,Aeson,To Json,Fromjson,我有一个API,它以以下形式返回JSON结果: { “数据”:[1,2,3] } 数据字段可以是两个不同记录的编码,如下所示: newtype ResultsTypeA=ResultsTypeA[ResultTypeA] newtype ResultsTypeB=ResultsTypeB[ResultTypeB] 当我从Haskell查询这个API时,我预先知道我是在处理ResultsTypeA还是ResultsTypeB,因为我在查询中明确要求它 我正在努力的部分是AesonToJSON和
{
“数据”:[1,2,3]
}
数据
字段可以是两个不同记录的编码,如下所示:
newtype ResultsTypeA=ResultsTypeA[ResultTypeA]
newtype ResultsTypeB=ResultsTypeB[ResultTypeB]
当我从Haskell查询这个API时,我预先知道我是在处理ResultsTypeA
还是ResultsTypeB
,因为我在查询中明确要求它
我正在努力的部分是AesonToJSON
和FromJSON
实例。因为两种结果类型A
和B
最终都是Int
的列表,所以我不能在json中使用模式匹配器,因为在这两种情况下我只能匹配A[Int]
这就是为什么我想到做以下事情:
newType apia响应=
蜂群反应{
数据::a
}
newtype ResultsTypeA=ResultsTypeA[ResultTypeA]
newtype ResultsTypeB=ResultsTypeB[ResultTypeB]
然而,我无法理解如何为上述内容编写ToJSON
和FromJSON
实例,因为现在ApiResponse
有一个类型参数,而在Aeson文档中似乎没有任何地方解释如何使用涉及的类型参数派生这些实例
另一种选择是避免使用类型参数,如下所示:
newtype结果=
结果类型A[结果类型A]
|结果类型B[结果类型B]
新型蜂毒反应=
蜂群反应{
数据::结果
}
在这种情况下,ToJSON
非常简单:
实例响应,其中
toJSON=genericToJSON$defaultOptions
但是来自JSON的让我们回到了无法决定结果类型A
和B
之间的问题
也有可能我完全做错了,还有第三种选择我看不到
- 如果ApiResponse上有一个类型参数,那么
FromJSON
/ToJSON
实例会是什么样子
- 有没有完全不同于上述内容的更好的替代方案来解决这个问题
因为结果类型A和B最终都是Int的列表,所以我不能在FromJSON中使用模式匹配器,因为在这两种情况下我只能匹配[Int]
如果您有一个参数化类型,并且您正在手工编写一个FromJSON
实例,那么您可以设置一个前提条件,即参数本身必须有一个FromJSON
实例
然后,在编写解析器时,可以将类型参数的解析器用作定义的一部分。像这样:
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson
data ApiResponse a =
ApiResponse {
_data :: a,
other :: Bool
}
instance FromJSON a => FromJSON (ApiResponse a) where
parseJSON = withObject "" $ \o ->
ApiResponse <$> o .: "data" -- we are using the parameter's FromJSON
<*> o .: "other"
如果我们在ghci中加载文件,我们可以向ApiResponse提供类型参数,并且:
如果还派生了泛型
,则还可以为ApiResponse
从JSON自动派生:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DerivingStrategies #-}
import Data.Aeson
import GHC.Generics
data ApiResponse a =
ApiResponse {
_data :: a,
other :: Bool
}
deriving stock Generic
deriving anyclass FromJSON
派生股票Generic
使GHC生成数据类型结构的表示,可用于派生其他类型类的实现,FromJSON
。对于那些通过通用
机制进行的推导,他们需要使用该方法
生成的实例的格式将是FromJSON a=>FromJSON(ApiResponse a)
,就像手写的一样。我们可以在ghci中再次检查:
ghci> :set -XPartialTypeSignatures
ghci> :set -Wno-partial-type-signatures
ghci> :instances ApiResponse _
instance FromJSON w => FromJSON (ApiResponse w)
instance Generic (ApiResponse w)
因为结果类型A和B最终都是Int的列表,所以我不能在FromJSON中使用模式匹配器,因为在这两种情况下我只能匹配[Int]
如果您有一个参数化类型,并且您正在手工编写一个FromJSON
实例,那么您可以设置一个前提条件,即参数本身必须有一个FromJSON
实例
然后,在编写解析器时,可以将类型参数的解析器用作定义的一部分。像这样:
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson
data ApiResponse a =
ApiResponse {
_data :: a,
other :: Bool
}
instance FromJSON a => FromJSON (ApiResponse a) where
parseJSON = withObject "" $ \o ->
ApiResponse <$> o .: "data" -- we are using the parameter's FromJSON
<*> o .: "other"
如果我们在ghci中加载文件,我们可以向ApiResponse提供类型参数,并且:
如果还派生了泛型
,则还可以为ApiResponse
从JSON自动派生:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DerivingStrategies #-}
import Data.Aeson
import GHC.Generics
data ApiResponse a =
ApiResponse {
_data :: a,
other :: Bool
}
deriving stock Generic
deriving anyclass FromJSON
派生股票Generic
使GHC生成数据类型结构的表示,可用于派生其他类型类的实现,FromJSON
。对于那些通过通用
机制进行的推导,他们需要使用该方法
生成的实例的格式将是FromJSON a=>FromJSON(ApiResponse a)
,就像手写的一样。我们可以在ghci中再次检查:
ghci> :set -XPartialTypeSignatures
ghci> :set -Wno-partial-type-signatures
ghci> :instances ApiResponse _
instance FromJSON w => FromJSON (ApiResponse w)
instance Generic (ApiResponse w)
我不认为有任何Haskell类型用于此专用目的是有意义的。有时候,最好在接口处使用s,然后将相关数据提取到Haskell类型中,而不必以任何方式近似JSON结构。我认为对于参数化类型,您应该能够从JSON派生,ToJSON
。生成的实例的格式为FromJSON a=>FromJSON(ApiResponse a)
。您还应该为ResultsTypeA
、ResultTypeA
等派生实例。ResultTypeA
的那些应该基于Int
的底层实例。这可以通过GeneralizedNewtypeDeriving
和DerivingStrategies
:派生新类型(FromJSON,ToJSON)
框架挑战:只使用[Int]
有什么不对?特别是考虑到“当我从Haskell查询此API时,我提前知道我是在处理ResultsTypeA
还是ResultsTypeB
,因为我在查询中明确要求它。”。如果您绝对必须有两种不同的类型,查询者只需应用appr