2
votes

I left the Akka world for a few months, and apparently I've lost my mojo. I am trying to write a web service that returns either an XML or a JSON document, based on the Accept header.

However, I can't get the Marshallers to work (returns 406, only accepts text/plain). This is what I have:

trait MyMarshallers extends DefaultJsonProtocol with SprayJsonSupport with ScalaXmlSupport {
  implicit def ec: ExecutionContext

  implicit val itemJsonFormat = jsonFormat3(MyPerson)


  def marshalCatalogItem(obj: MyPerson): NodeSeq =
    <MyPerson>
      <id>
        {obj.ID}
      </id>
      <name>
        {obj.Name}
      </name>
      <age>
        {obj.Age}
      </age>
    </MyPerson>

  def marshalCatalogItems(items: Iterable[MyPerson]): NodeSeq =
    <Team>
      {items.map(marshalCatalogItem)}
    </Team>

  implicit def catalogXmlFormat = Marshaller.opaque[Iterable[MyPerson], NodeSeq](marshalCatalogItems)

  implicit def catalogItemXmlFormat = Marshaller.opaque[MyPerson, NodeSeq](marshalCatalogItem)

  implicit val catalogMarshaller: ToResponseMarshaller[Iterable[MyPerson]] = Marshaller.oneOf(
    Marshaller.withFixedContentType(MediaTypes.`application/json`) { catalog ⇒
      HttpResponse(entity = HttpEntity(ContentType(MediaTypes.`application/json`),
        catalog.map(i ⇒ MyPerson(i.ID, i.Name, i.Age))
          .toJson.compactPrint))
    }
    ,
    Marshaller.withOpenCharset(MediaTypes.`application/xml`) { (catalog, charset) ⇒
      HttpResponse(entity = HttpEntity.CloseDelimited(ContentType(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`),
        Source.fromFuture(Marshal(catalog.map(i => MyPerson(i.ID, i.Name, i.Age)))
          .to[NodeSeq])
          .map(ns ⇒ ByteString(ns.toString()))
      )
      )
    }
  )
}

and my route looks like this:

class MyService extends MyMarshallers {
  implicit val system = ActorSystem("myService")
  implicit val materializer = ActorMaterializer()
  implicit val ec: ExecutionContext = system.dispatcher

    ...
    def route = ...
        (get & path("teams")) {
              parameters('name.as[String]) { id =>
                complete {
                  getTeam(id)
                }

              }
      ...

}

And if I request /, I get plain text, but if I request application/xml or application/json, I get a 406.

How does AKKA-HTTP decide what content types it will accept? I just updated everything to Akka 2.4.6.

1

1 Answers

0
votes

Sorry, I figured it out. The above code had two issues, one of them fixed the problem:

  • Issue 1 - changed to use fromIterator instead of fromFuture in the XML marshaller.
  • Issue 2 - my getTeam() method was returning an Iterable. I changed that to a Seq.

Now everything works.