Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

akka http to use Json Support and xmlsupport

I want to print data in xml format when department is "hr"but data in json format when department is "tech" .

Can we use spray-json Support https://doc.akka.io/docs/akka-http/current/common/json-support.html and XML support https://doc.akka.io/docs/akka-http/current/common/xml-support.html together

 private[rest] def route =
  (pathPrefix("employee") & get) {
    path(Segment) { id =>
      parameters('department ? "") { (flag) =>
        extractUri { uri =>
          complete {
            flag match {
              case "hr": =>  {

            HttpEntity(MediaTypes.`application/xml`.withCharset(HttpCharsets.`UTF-8`),"hr department")
            }
              case "tech" =>{
                HttpEntity(ContentType(MediaTypes.`application/json`), mapper.writeValueAsString("tech department"))

              }

            }
          }
        }
      }
    }
  }

Solution I tried I tried the below by using by using JsonProtocols and ScalaXmlSupport I get the compile error expected ToResponseMarshallable but found Department

case class department(name:String)
private[rest] def route =
  (pathPrefix("employee") & get) {
    path(Segment) { id =>
      parameters('department ? "") { (flag) =>
        extractUri { uri =>
          complete {
            flag match {
              case "hr": =>  {

            complete(department(name =flag))
            }
              case "tech" =>{
                complete(department(name =flag))

              }

            }
          }
        }
      }
    }
  }
like image 729
coder25 Avatar asked Jul 09 '18 14:07

coder25


People also ask

Does Akka HTTP support integration with other JSON libraries?

Integration with other JSON libraries are supported by the community. See the list of current community extensions for Akka HTTP. The SprayJsonSupport trait provides a FromEntityUnmarshaller [T] and ToEntityMarshaller [T] for every type T that an implicit spray.json.RootJsonReader and/or spray.json.RootJsonWriter (respectively) is available for.

What is jacksonsupport in Akka HTTP?

The support trait is called (unsurprisingly) JacksonSupport, so the code will look like this: Akka HTTP is a magical library for spinning up HTTP services very quickly. In this article, you learned how to use not 1, but 3 different libraries for serializing and deserializing JSON auto-magically with directives.

Does Akka-HTTP-JSON support JSON (UN)marshalling?

akka-http-json provides JSON (un)marshalling support for Akka HTTP via the following JSON libraries: The artifacts are published to Maven Central. libraryDependencies ++= Seq ( "de.heikoseeberger" %% "akka-http-circe" % AkkaHttpJsonVersion , ... )

How do I integrate spray-JSON with Akka HTTP?

Integration with spray-json is provided out of the box through the akka-http-spray-json module. Integration with other JSON libraries are supported by the community. See the list of current community extensions for Akka HTTP.


2 Answers

I think there are several issues you have to overcome to achieve what you want:

  1. You want to customize response type basing on the request parameters. It means standard implicit-based marshaling will not work for you, you'll have to do some explicit steps

  2. You want to marshal into an XML-string some business objects. Unfortunately, ScalaXmlSupport that you referenced does not support this, it can marshal only an XML-tree into a response. So you'll need some library that can do XML serialization. One option would be to use jackson-dataformat-xml with jackson-module-scala. It also means you'll have to write your own custom Marshaller. Luckily it is not that hard.

So here goes some simple code that might work for you:

import akka.http.scaladsl.marshalling.{ToResponseMarshallable, Marshaller}

// json marshalling
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import spray.json._
import spray.json.DefaultJsonProtocol._
implicit val departmentFormat = DefaultJsonProtocol.jsonFormat1(department)
val departmentJsonMarshaller = SprayJsonSupport.sprayJsonMarshaller[department]

// xml marshalling, need to write a custom Marshaller
// there are several MediaTypes for XML such as `application/xml` and `text/xml`, you'll have to choose the one you need.
val departmentXmlMarshaller = Marshaller.StringMarshaller.wrap(MediaTypes.`application/xml`)((d: department) => {
  import com.fasterxml.jackson.dataformat.xml.XmlMapper
  import com.fasterxml.jackson.module.scala.DefaultScalaModule
  val mapper = new XmlMapper()
  mapper.registerModule(DefaultScalaModule)
  mapper.writeValueAsString(d)
})


private val route =
  (pathPrefix("employee") & get) {
    path(Segment) { id =>
      parameters('department ? "") { (flag) =>
        extractUri { uri => {
          flag match {
            case "hr" => {
              // explicitly use the XML marshaller 
              complete(ToResponseMarshallable(department(name = flag))(departmentXmlMarshaller))
            }
            case "tech" => {
              // explicitly use the JSON marshaller 
              complete(ToResponseMarshallable(department(name = flag))(departmentJsonMarshaller))
            }
          }
        }
        }
      }
    }
  }

Note that for Jackson XML serializer to work correctly the department class should be a top level class or you'll get a cryptic error about bad root name.

like image 108
SergGr Avatar answered Oct 03 '22 22:10

SergGr


Akka Http already has built in content type negotiation. Ideally you should just use that by having a marshaller that knows how to turn your department into either xml or json and having the client set the Accept header.

However it sounds like maybe you can't get your client to do that, so here's what you can do, assuming you have already made a ToEntityMarshaller[department] for both xml and json using ScalaXmlSupport and SprayJsonSupport.

val toXmlEntityMarshaller: ToEntityMarshaller[department] = ???
val toJsonEntityMarshaller: ToEntityMarshaller[department] = ???
implicit val departmentMarshaller = Marshaller.oneOf(toJsonEntityMarshaller, toXmlEntityMarshaller)

def route =
  parameters("department") { departmentName =>
    // capture the Accept header in case the client did request one
    optionalHeaderValueByType[Accept] { maybeAcceptHeader => 
      mapRequest ( _
        .removeHeader(Accept.name)
        // set the Accept header based on the department
        .addHeader(maybeAcceptHeader.getOrElse(
          Accept(departmentName match {
            case "hr" ⇒ MediaTypes.`application/xml`
            case "tech" ⇒ MediaTypes.`application/json`
          })
        ))
      ) (
        // none of our logic code is concerned with the response type
        complete(department(departmentName))
        )
    }
  }
like image 23
kag0 Avatar answered Oct 03 '22 22:10

kag0