Search code examples
scalaakkamarshallingakka-httpspray

Unable to JSON Marshal B defined as List[A] using AKKA-Http and Spray-Json


as stated in the title, I'm not able to marshal List[A] into the proper Json (Array of Objects).

I'm using AKKA-Http and Spray-Json.

I defined two case classes:

final case class Item(id: String, pid: String, title: String)
final case class Items(items: List[Item])

And on that call GET http://localhost:8080/item received:

class ItemEndpoint extends Directives with ItemJsonSupport {

  val route: Route = {
    path("item") {
      get {
        parameters("page".?, "size".?) {
          (page, size) => (page, size) match {
            case (_, _) => 
              onSuccess(Server.requestHandler ? GetItemsRequest){
                case response: Items =>
                  complete(response)
                case _ =>
                  complete(StatusCodes.InternalServerError)
              }
          }
        }
      }
    }
  }       

}

GetItemsRequest is called. The latter is defined as

case class GetItemsRequest 

And the RequestHandler as

class RequestHandler extends Actor with ActorLogging {

  var items : Items = _

  def receive: Receive = {
    case GetItemsRequest =>
      items = itemFactory.getItems
      sender() !  items
  }

}

Where getItems performs a query on Cassandra via Spark

def getItems() : Items = {
    val query = sc.sql("SELECT * FROM mydb")
    Items(query.map(row => Item(row.getAs[String]("item"), 
                           row.getAs[String]("pid"), row.getAs[String]("title")
    )).collect().toList)
}

returning Items containing List[Item]. All the objects are printed correctly (some of them have null fields).

Using ItemJsonFormatter

trait ItemJsonSupport extends SprayJsonSupport with DefaultJsonProtocol {
  implicit val itemFormat: RootJsonFormat[Item] = jsonFormat3(Item)
  implicit val itemsFormat: RootJsonFormat[Items] = jsonFormat1(Items)
}

Leads to the following error:

[simple-rest-system-akka.actor.default-dispatcher-9] [akka.actor.ActorSystemImpl(simple-rest-system)] Error during processing of request: 'requirement failed'. Completing with 500 Internal Server Error response. To change default exception handling behavior, provide a custom ExceptionHandler. java.lang.IllegalArgumentException: requirement failed

I tried catching the exception and work on it but I haven't obtained more intel on the problem.

I followed AKKA DOCS on marshalling.

When doing the same thing, but getting only 1 item, it works just fine, I obtain a json containing all Item's parameters well formatted.

{
    "id": "78289232389",
    "pid": "B007ILCQ8I",
    "title": ""
}

Even looking at other related Q/A I was not able to find an answer, so What's Causing this? How can I fix it?


Solution

  • All the objects are printed correctly (some of them have null fields).

    The exception could be thrown because getItems is returning Item objects that have one or more null field values.

    One way to handle this is to replace nulls with empty strings:

    def rowToItem(row: Row): Item = {
      Item(Option(row.getAs[String]("item")).getOrElse(""),
           Option(row.getAs[String]("pid")).getOrElse(""),
           Option(row.getAs[String]("title")).getOrElse(""))
    }
    
    def getItems(): Items = {
      val query = sc.sql("SELECT * FROM mydb")
      Items(query.map(row => rowToItem(row)).collect().toList)
    }