【问题标题】:Tapir Custom Codec貘自定义编解码器
【发布时间】:2020-06-27 18:41:06
【问题描述】:

我被困在一个地方,我正在使用 scala、tapi 和 circe。

sealed abstract class S1Error extends Product with Serializable
object S1Error {
  final case class SError(error: SMError) extends S1Error
}
sealed abstract class SMError(message: String)
object SMError {
  final case class SVError(message: String) extends SMError(message)
}

对于tapir errorOut 我正在使用这个

val schemaVersionError: EndpointOutput.StatusMapping[SError] = statusMappingValueMatcher(
      StatusCode.BadRequest,
      jsonBody[SError]
        .description("XXXX.")
    ) {
      case SMError(SVError(_)) => true
      case _                                  => false
    }

现在由于这种结构,我得到的 API 结果是

{
    "error": {
        "SVError": {
            "message": "XXXXG"
        }
    }
}

理想情况下我希望得到回应

"message": "XXXXG"

我无法更改错误结构。 有没有办法使用自定义编解码器包装此错误以获取所需的结果。

【问题讨论】:

    标签: scala codec circe tapir


    【解决方案1】:

    Tapir 编解码器源自 Circe 的解码器和编码器。

    你看到的是circe编码case类的默认方式。

    Circe 提供了按照您在 circe-generic-extras 中使用 deriveUnwrappedEncoder 描述的方式对案例类进行编码的可能性。不幸的是,它不能为 SMError 编译(可能派生机制被您的抽象类层次结构混淆了)。

    您可以做的只是手动创建编码器:

    sealed abstract class S1Error extends Product with Serializable
    
    object S1Error {
      final case class SError(error: SMError) extends S1Error
    
      implicit val encoder: Encoder[SError] = Encoder[SMError].contramap(_.error)
      // or you can use deriveUnwrappedEncoder from circe-generic-extras:
      // implicit val encoder: Encoder[SError] = deriveUnwrappedEncoder
    }
    
    //I also needed to make message a field in SMError
    sealed abstract class SMError(val message: String)
    object SMError {
      final case class SVError(override val message: String) extends SMError(message)
    
      implicit val encoder: Encoder[SMError] = Encoder.encodeJsonObject.contramap{s => JsonObject("message" -> s.message.asJson)}
    }
    

    响应现在看起来像:

    {
        message": "XXXXG"
    }
    

    【讨论】:

    • Krzysztof Atłasik 你能解释一下这种编码是如何工作的吗?
    • 另外,我有final case class Bulk(accepted: Seq[String], failed: Seq[FailedRecord]) final case class FailedRecord(record: Option[String], error: S1Error) 我如何确保当我为Bulk 打印json 时,我得到S1Error 的正确编码消息
    • 这样在这种情况下我应该得到{ "accepted": [ "a", "b", "c" ], "failed": [ { "record": "jk", "error": "xxx" } ] }
    • 我通过 implicit val signalErrorEncoder: Encoder[S1Error] = Encoder.instance { case foo@ S1Error. SError(_) => foo.asJson } 使其工作但只要final case class Bulk(accepted: Seq[String], failed: Seq[FailedRecord]) final case class FailedRecord(record: Option[String], error: S1Error)implicit val signalErrorEncoder: Encoder[S1Error] = Encoder.instance { case foo@ S1Error. SError(_) => foo.asJson } 在 S1Error 对象内就可以工作但在我的情况下 BulkFailed 都是在不同的类中,上述解决方案不起作用
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-24
    • 1970-01-01
    • 1970-01-01
    • 2017-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多