如何使用xml或json进行scala / akka / spray Web服务响应,具体取决于ACCEPT标头

时间:2013-10-15 19:35:56

标签: xml json akka spray spray-json

我为地球上的json数据编写了最丑陋的Marshaller。尽管它很可怕,它仍然有效。问题是,当我添加代码来编组xml时,它只会封送xml并退出接受json。有人能举个例子说明一个更好的方法吗?我只是希望能够根据提供的ACCEPT标头将我的对象编组并解组为xml和json。

trait StupidFormats extends DefaultJsonProtocol with SprayJsonSupport with MetaMarshallers {
  val formatter: DateTimeFormatter = DateTimeFormat.forPattern("MM/dd/yyyy HH:mm:ss ZZZ").withLocale(
    Locale.ROOT).withChronology(ISOChronology.getInstanceUTC)
  val periodFormatter: PeriodFormatter = new PeriodFormatterBuilder().printZeroAlways().minimumPrintedDigits(1)
    .appendDays().appendSuffix(" days").appendSeparator(", ").printZeroAlways().minimumPrintedDigits(2).appendHours()
    .appendSeparator(":").printZeroAlways().minimumPrintedDigits(2).appendMinutes().appendSeparator(":").appendSeconds()
    .toFormatter

  /*implicit val dataResultXmlMarshaller: Marshaller[DataResult] =
    Marshaller.delegate[DataResult, NodeSeq](`text/xml`, `application/xml`, `text/html`, `application/xhtml+xml`)
    { d: DataResult =>
      <DataResult>
        <ApplicationVersion>{d.applicationVersion}</ApplicationVersion>
        <Datestamp>{d.dateStamp}</Datestamp>
        <IsHealthy>{d.isHealthy}</IsHealthy>
        <MemFree>{d.memFree}</MemFree>
        <MemMax>{d.memMax}</MemMax>
        <MemPeak>{d.memPeak}</MemPeak>
        <MemUsed>{d.memUsed}</MemUsed>
        <ServiceHostIp>{d.serviceHostIp}</ServiceHostIp>
        <Uptime>{periodFormatter.print(d.uptime)}</Uptime>
        <OptionalElements></OptionalElements>
      </DataResult>
    }*/

  implicit object DataResultJsonFormat extends RootJsonFormat[DataResult] {
    def write(d: DataResult) = {
      JsObject(
        "ApplicationVersion" -> JsString(d.applicationVersion),
        "Datestamp" -> JsNumber(d.dateStamp),
        "IsHealthy" -> JsBoolean(d.isHealthy),
        "MemFree" -> JsNumber(d.memFree),
        "MemMax" -> JsNumber(d.memMax),
        "MemPeak" -> JsNumber(d.memPeak),
        "MemUsed" -> JsNumber(d.memUsed),
        "ServiceHostIp" -> JsString(d.serviceHostIp),
        "Uptime" -> JsString(periodFormatter.print(d.uptime)),
        "OptionalElements" -> JsObject (
          "OptionalElement" -> (
            for (oe <- d.optionalElements if d.optionalElements.size > 0) yield {
              JsObject (
                "DataType" -> JsString(oe.dataType),
                "Description" -> JsString(oe.description),
                "LastUpdated" -> JsString(formatter.print(oe.lastUpdated)),
                "Name" -> JsString(oe.name),
                "Value" -> JsString(oe.value)
              )
            }
            ).collect { case v: JsObject => v.fields }.toJson
        )
      )
    }

    def read(js: JsValue) = {
      js.asJsObject.getFields("ApplicationVersion", "DateStamp", "IsHealthy", "MemFree", "MemMax", "MemPeak",
        "MemUsed", "ServiceHostIP", "Uptime", "OptionalElement") match {
        case Seq(
        JsString(applicationVersion),
        JsNumber(dateStamp),
        JsBoolean(isHealthy),
        JsNumber(memFree),
        JsNumber(memMax),
        JsNumber(memPeak),
        JsNumber(memUsed),
        JsString(serviceHostIp),
        JsString(uptime),
        JsArray(optionalElements)
        ) => { DataResult (
          applicationVersion,
          dateStamp.toLong,
          isHealthy,
          memFree.toLong,
          memMax.toLong,
          memPeak.toLong,
          memUsed.toLong,
          serviceHostIp,
          Period.parse(uptime, periodFormatter),
          (for (oe <- optionalElements if optionalElements.size > 0) yield {
            oe.asJsObject.getFields("DataType", "Description", "LastUpdated", "Name", "Value") match {
              case Seq(
              JsString(dataType),
              JsString(description),
              JsString(lastUpdated),
              JsString(name),
              JsString(value)
              ) => OptionalElement (
                dataType,
                description,
                DateTime.parse(lastUpdated, formatter),
                name, value)
            }
          }).toList)
        }
      }
    }
  }
}

1 个答案:

答案 0 :(得分:1)

如果您要提供REST界面,最好的方法是根据路径请求的扩展名.json.xml来确定类型。这样您的API将更容易使用。 HTTP/1.1 Accept header documentation表明内容协商有点复杂。

回答您的问题,Spray支持基本级别的内容协商。 HttpMessage类包含一个方法isMediaTypeAccepted,该方法也被同一类中的许多其他方法使用。

当然HttpMessage也会在请求和响应方面重复使用。希望这能指明你的正确方向。