我正在尝试从Haskell服务器获取一些JSON数据,但我遇到了Respondeable实例,以及一般的Affjax。我已经使用Data.Argonaut.Generic.Aeson(GA)定义了EncodeJson + DecodeJson,但我无法弄清楚如何将其与Respondeable实例相匹配,并且它来自响应函数。
它给出了错误“无法匹配类型外部类型与Json”但是可以重用我的decodeJson实例而无需手动创建任何其他内容吗?也许通过创建一个IsForeign实例,但使用GA.decodeJson?我只是不确定如何去做。我已经看到它是如何在https://github.com/purescript/purescript-foreign/blob/master/examples/Complex.purs手动完成的,但我有复杂的类型需要与我的Haskell JSON输出相匹配,手动操作会非常痛苦。
我正在使用purescript 10.7,Affjax 3.02和argonaut 2.0.0,以及argonaut-generic-codecs 5.1.0。谢谢!
testAffjax :: forall eff. Aff (ajax :: AJAX | eff) (Answer)
testAffjax = launchAff do
res <- affjax $ defaultRequest { url = "/", method = Left GET }
pure res.response
data Answer = Answer {
_answer :: String
, _isCorrect :: Boolean
, _hint :: String
}
{- PROBLEM -}
instance respondableAnswer :: Respondable Answer where
responseType = Tuple Nothing JSONResponse
fromResponse = GA.decodeJson {- Error here -}
derive instance genericAnswer :: Generic Answer
instance showAnswer :: Show Answer where
show = gShow
instance encodeAnswer :: EncodeJson Answer where
encodeJson = GA.encodeJson
instance decodeAnswer :: DecodeJson Answer where
decodeJson = GA.decodeJson
答案 0 :(得分:7)
您正在寻找的是一个适应JSON解码器的功能:
decodeJson :: forall a. Json -> Either String a
使用F
而不是Either
返回。 F
是Data.Foreign
Except MultipleErrors a
中定义的同义词。要做到这一点,我们需要:
String
错误翻译为MultipleErrors
Either
转换为Except
MultipleErrors
是Data.Foreign
中定义的另一个同义词,这次是NonEmptyList ForeignError
。 Looking at ForeignError
还有一个名为ForeignError
的构造函数,它允许我们提供一些字符串消息。这使我们需要创建一个NonEmptyList
,这很简单:
remapError = pure <<< ForeignError
NonEmptyList
为Applicative
,因此我们可以使用pure
创建一个单元素列表。
从Either
转到Except
也很简单。再次looking at the definitions in Pursuit我们可以看到:
newtype ExceptT m e a = ExceptT (m (Either e a))
type Except = ExceptT Identity
所以ExceptT
已经只是一个花哨的Either
,给了我们:
eitherToExcept = ExceptT <<< pure
此处pure
将Either e a
提升为m (Either e a)
Except
m ~ Identity
。
所以现在我们可以采用这些东西,并为Affjax响应制作一个通用的解码JSON&#34;功能:
decodeJsonResponse :: forall a. DecodeJson a => Json -> F a
decodeJsonResponse =
ExceptT <<< pure <<< lmap (pure <<< ForeignError) <<< decodeJson
此处发生的唯一其他事情是我们使用lmap
映射Either
的左侧部分,以执行错误消息类型转换位。
我们现在可以使用Kleisli合成((<=<)
)将此decodeJsonResponse
与原始fromResponse
链接起来ResponseContent -> F Json
:
instance respondableAnswer :: Respondable Answer where
responseType = Tuple (Just applicationJSON) JSONResponse
fromResponse = decodeJsonResponse <=< fromResponse
以下是使用Answer
类型的完整示例:
module Main where
import Prelude
import Control.Monad.Aff (Aff)
import Control.Monad.Except (ExceptT(..))
import Data.Argonaut (class DecodeJson, class EncodeJson, Json, decodeJson)
import Data.Argonaut.Generic.Argonaut as GA
import Data.Bifunctor (lmap)
import Data.Foreign (F, ForeignError(..))
import Data.Generic (class Generic, gShow)
import Data.Maybe (Maybe(..))
import Data.MediaType.Common as MediaType
import Data.Tuple (Tuple(..))
import Network.HTTP.Affjax as AX
import Network.HTTP.Affjax.Response as AXR
testAffjax :: forall eff. Aff (ajax :: AX.AJAX | eff) Answer
testAffjax = _.response <$> AX.get "/"
newtype Answer = Answer
{ _answer :: String
, _isCorrect :: Boolean
, _hint :: String
}
derive instance genericAnswer :: Generic Answer
instance showAnswer :: Show Answer where
show = gShow
instance encodeAnswer :: EncodeJson Answer where
encodeJson = GA.encodeJson
instance decodeAnswer :: DecodeJson Answer where
decodeJson = GA.decodeJson
instance respondableAnswer :: AXR.Respondable Answer where
responseType = Tuple (Just MediaType.applicationJSON) AXR.JSONResponse
fromResponse = decodeJsonResponse <=< AXR.fromResponse
decodeJsonResponse :: forall a. DecodeJson a => Json -> F a
decodeJsonResponse =
ExceptT <<< pure <<< lmap (pure <<< ForeignError) <<< decodeJson