簡體   English   中英

Purescript重用Argonaut JSON解碼為Affjax響應

[英]Purescript Reuse Argonaut JSON Decoding for Affjax Respondeable

我正在嘗試從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

您正在尋找的是一個適應JSON解碼器的功能:

decodeJson :: forall a. Json -> Either String a

使用F而不是Either返回。 FData.Foreign定義的同義詞,用於Except MultipleErrors a Data.Foreign Except MultipleErrors a 要做到這一點,我們需要:

  1. 將我們的String錯誤轉換為MultipleErrors
  2. Either轉換為Except

MultipleErrorsData.Foreign定義的另一個同義詞,這次是NonEmptyList ForeignError 看看ForeignError有一個名為ForeignError的構造ForeignError ,它允許我們提供一些字符串消息。 這讓我們需要創建一個NonEmptyList ,這非常簡單:

remapError = pure <<< ForeignError

NonEmptyListApplicative ,因此我們可以使用pure創建一個單元素列表。

EitherExcept也很簡單。 看看Pursuit中的定義,我們可以看到:

newtype ExceptT m e a = ExceptT (m (Either e a))
type Except = ExceptT Identity

所以ExceptT只是看中Either已經,給我們:

eitherToExcept = ExceptT <<< pure

這里的pure是將Either ea提升為m (Either ea)Except m ~ Identity

所以現在我們可以采用這些東西,並制作一個通用的“為Affjax響應解碼JSON”功能:

decodeJsonResponse :: forall a. DecodeJson a => Json -> F a
decodeJsonResponse =
  ExceptT <<< pure <<< lmap (pure <<< ForeignError) <<< decodeJson

在這里發生的唯一另一件事是我們使用lmap映射Either的左側部分,以執行錯誤消息類型轉換位。

我們現在可以使用Kleisli組合( (<=<) )將此decodeJsonResponse與原始fromResponse decodeJsonResponse起來,它將執行初始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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM