akka 从层次结构接收对象列表的终结点

b1uwtaje  于 2022-11-06  发布在  其他
关注(0)|答案(2)|浏览(125)

Akka Http中的端点如下所示:

pathPrefix("somePath" / Segment) { someData =>
  post {
    entity(as[SMS]) { sms =>
      // some code here ...
      complete(StatusCodes.OK)
    }
  }
}

而SMS的定义是:

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

詹森:

[ 
  {"numForm": "123 456", "message": "sms message"},
  {"emailFrom": "some@mail.com", "email message"}
]
yrefmtwq

yrefmtwq1#

假设你在akka-http - spray-json中使用默认的一个json序列化库-你在组合几个json阅读器方面受到很大的限制(根据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:(在指定确切的库之后):
有几个选项:
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
1.创建自定义解码器,该解码器显式检查需要的字段-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)
}

相关问题