从层次结构中接收对象列表的端点
Endpoint receiving a list of objects from a hierarchy
Akka Http 中的端点如下所示:
pathPrefix("somePath" / Segment) { someData =>
post {
entity(as[SMS]) { sms =>
// some code here ...
complete(StatusCodes.OK)
}
}
}
短信定义为:
sealed trait Message
case class SMS(numFrom: String, message:String) extends Message
case class Email(emailFrom: String, message: String) extends Message
如果我想收到 SMS
的列表,我可以执行以下操作:
type SMSList = List[SMS]
...
pathPrefix("somePath" / Segment) { someData =>
post {
entity(as[SMSList]) { listOfSMSs =>
// some code here ...
complete(StatusCodes.OK)
}
}
}
如果我想同时收到短信和邮件列表怎么办?
我已经试过了,但没用:
type MessageList = List[Message]
pathPrefix("somePath" / Segment) { someData =>
post {
entity(as[MessageList]) { listOfMessages =>
// some code here ...
complete(StatusCodes.OK)
}
}
}
是否可以接收属于同一层次结构的对象列表?
图书馆:
circe = 0.13.0
heikoseeberger = 1.35.3
akka http = 10.2.3
Json:
[
{"numForm": "123 456", "message": "sms message"},
{"emailFrom": "some@mail.com", "email message"}
]
假设您使用的是 akka-http 中的默认 json 序列化库 - spray-json - 您在组合多个 json reader 时受到很大限制s(根据 official page 和源代码)。你能做的最好的可能是为 Message
.
手动编写一些格式化程序(或只是 reader)
import spray.json.DefaultJsonProtocol._
import spray.json._
implicit val smsFormat: JsonFormat[SMS] = jsonFormat2(SMS)
implicit val emailFormat: JsonFormat[Email] = jsonFormat2(Email)
implicit val messageFormat: JsonFormat[Message] = new JsonFormat[Message] {
override def read(json: JsValue): Message = json match {
case sms@JsObject(_) if sms.fields.contains("numFrom") => smsFormat.read(sms)
case email@JsObject(_) if email.fields.contains("emailFrom") => emailFormat.read(email)
case _ => deserializationError("object expected")
}
override def write(obj: Message): JsValue = obj match {
case sms: SMS => sms.toJson
case email: Email => email.toJson
case _ => throw new RuntimeException("Houston, we have a problem")
}
}
我还建议您看一下 circe
库,它的代码组合性更强。它也很容易与 akka-http 集成。
更新 1:(在指定确切的库之后):
选项很少:
合并几个解码器
import io.circe.generic.auto._
import io.circe.{Decoder, HCursor}
class CirceExample extends App {
sealed trait Message
case class SMS(numFrom: String, message: String) extends Message
case class Email(emailFrom: String, message: String) extends Message
val smsDecoder = implicitly[Decoder[SMS]]
val emailDecoder = implicitly[Decoder[Email]]
val messageDecoder: Decoder[Message] = (c: HCursor) => smsDecoder(c).orElse(emailDecoder(c))
}
这很容易实现,因为Decoder
的解码结果是Either
。
创建自定义解码器,明确检查所需字段 - documentation。这种方法有点类似于前面使用 spray-json.
的示例
更新 2:
import cats.syntax.functor._
import io.circe.Decoder
import io.circe.generic.auto._
import io.circe.parser._
object CirceExample extends App {
sealed trait Message
case class SMS(numFrom: String, message: String) extends Message
case class Email(emailFrom: String, message: String) extends Message
implicit val messageDecoder: Decoder[Message] = List[Decoder[Message]](Decoder[SMS].widen, Decoder[Email].widen).reduceLeft(_ or _)
// or without list..
//implicit val messageDecoder: Decoder[Message] = Decoder[SMS].widen or Decoder[Email].widen
val payload = """[{"emailFrom":"a","message":"b"}]"""
val result = decode[List[Message]](payload)
println(result)
}
这里有几个步骤,所以我会尽量保持简单。
首先,我们需要为 Email
和 SMS
:
定义编码器和解码器
sealed trait Message
@JsonCodec case class SMS(numFrom: String, message:String) extends Message
@JsonCodec case class Email(emailFrom: String, message: String) extends Message
object Message {
implicit val decodeA: Decoder[Message] = Decoder[SMS].map[Message](identity).or(Decoder[Email].map[Message](identity))
implicit val encodeA: Encoder[Message] = Encoder.instance {
case b @ SMS(_, _) => b.asJson
case c @ Email(_, _) => c.asJson
}
}
我在 this link 中找到了这个,之前该线程的所有尝试都没有成功。
让我们继续。我们必须在单独的模块中找到它。否则,我们会得到 enable macro paradise to expand macro annotations
错误。看完 post,基本上解释了宏不能在同一个模块中声明和使用,我将上面的代码移动到另一个模块中,使代码能够编译。
有了上面代码的独立模块后,我们需要定义一个新模块,以依赖第一个模块。
现在,在最后一个模块中,我们需要首先导入:
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport.unmarshaller
然后我们可以创建一个路由:
val routes = pathPrefix("somePath" / Segment) { someData =>
post {
entity(as[List[Message]]) { listOfMessages =>
// some code here ...
complete(StatusCodes.OK)
}
}
}
Akka Http 中的端点如下所示:
pathPrefix("somePath" / Segment) { someData =>
post {
entity(as[SMS]) { sms =>
// some code here ...
complete(StatusCodes.OK)
}
}
}
短信定义为:
sealed trait Message
case class SMS(numFrom: String, message:String) extends Message
case class Email(emailFrom: String, message: String) extends Message
如果我想收到 SMS
的列表,我可以执行以下操作:
type SMSList = List[SMS]
...
pathPrefix("somePath" / Segment) { someData =>
post {
entity(as[SMSList]) { listOfSMSs =>
// some code here ...
complete(StatusCodes.OK)
}
}
}
如果我想同时收到短信和邮件列表怎么办? 我已经试过了,但没用:
type MessageList = List[Message]
pathPrefix("somePath" / Segment) { someData =>
post {
entity(as[MessageList]) { listOfMessages =>
// some code here ...
complete(StatusCodes.OK)
}
}
}
是否可以接收属于同一层次结构的对象列表?
图书馆:
circe = 0.13.0
heikoseeberger = 1.35.3
akka http = 10.2.3
Json:
[
{"numForm": "123 456", "message": "sms message"},
{"emailFrom": "some@mail.com", "email message"}
]
假设您使用的是 akka-http 中的默认 json 序列化库 - spray-json - 您在组合多个 json reader 时受到很大限制s(根据 official page 和源代码)。你能做的最好的可能是为 Message
.
import spray.json.DefaultJsonProtocol._
import spray.json._
implicit val smsFormat: JsonFormat[SMS] = jsonFormat2(SMS)
implicit val emailFormat: JsonFormat[Email] = jsonFormat2(Email)
implicit val messageFormat: JsonFormat[Message] = new JsonFormat[Message] {
override def read(json: JsValue): Message = json match {
case sms@JsObject(_) if sms.fields.contains("numFrom") => smsFormat.read(sms)
case email@JsObject(_) if email.fields.contains("emailFrom") => emailFormat.read(email)
case _ => deserializationError("object expected")
}
override def write(obj: Message): JsValue = obj match {
case sms: SMS => sms.toJson
case email: Email => email.toJson
case _ => throw new RuntimeException("Houston, we have a problem")
}
}
我还建议您看一下 circe
库,它的代码组合性更强。它也很容易与 akka-http 集成。
更新 1:(在指定确切的库之后):
选项很少:
合并几个解码器
import io.circe.generic.auto._ import io.circe.{Decoder, HCursor} class CirceExample extends App { sealed trait Message case class SMS(numFrom: String, message: String) extends Message case class Email(emailFrom: String, message: String) extends Message val smsDecoder = implicitly[Decoder[SMS]] val emailDecoder = implicitly[Decoder[Email]] val messageDecoder: Decoder[Message] = (c: HCursor) => smsDecoder(c).orElse(emailDecoder(c)) }
这很容易实现,因为Decoder
的解码结果是Either
。创建自定义解码器,明确检查所需字段 - documentation。这种方法有点类似于前面使用 spray-json.
的示例
更新 2:
import cats.syntax.functor._
import io.circe.Decoder
import io.circe.generic.auto._
import io.circe.parser._
object CirceExample extends App {
sealed trait Message
case class SMS(numFrom: String, message: String) extends Message
case class Email(emailFrom: String, message: String) extends Message
implicit val messageDecoder: Decoder[Message] = List[Decoder[Message]](Decoder[SMS].widen, Decoder[Email].widen).reduceLeft(_ or _)
// or without list..
//implicit val messageDecoder: Decoder[Message] = Decoder[SMS].widen or Decoder[Email].widen
val payload = """[{"emailFrom":"a","message":"b"}]"""
val result = decode[List[Message]](payload)
println(result)
}
这里有几个步骤,所以我会尽量保持简单。
首先,我们需要为 Email
和 SMS
:
sealed trait Message
@JsonCodec case class SMS(numFrom: String, message:String) extends Message
@JsonCodec case class Email(emailFrom: String, message: String) extends Message
object Message {
implicit val decodeA: Decoder[Message] = Decoder[SMS].map[Message](identity).or(Decoder[Email].map[Message](identity))
implicit val encodeA: Encoder[Message] = Encoder.instance {
case b @ SMS(_, _) => b.asJson
case c @ Email(_, _) => c.asJson
}
}
我在 this link 中找到了这个,之前该线程的所有尝试都没有成功。
让我们继续。我们必须在单独的模块中找到它。否则,我们会得到 enable macro paradise to expand macro annotations
错误。看完
有了上面代码的独立模块后,我们需要定义一个新模块,以依赖第一个模块。
现在,在最后一个模块中,我们需要首先导入:
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport.unmarshaller
然后我们可以创建一个路由:
val routes = pathPrefix("somePath" / Segment) { someData =>
post {
entity(as[List[Message]]) { listOfMessages =>
// some code here ...
complete(StatusCodes.OK)
}
}
}