Я использую Salat в своем проекте Scala/Scalatra для сериализации объектов MongoDB. Это общий вопрос, поскольку я пытаюсь извлечь сложный объект MongoDB из своей базы данных, который включает в себя различные собственные хэши и массивы, для которых я создал классы case. Я мог бы прокомментировать поле за полем и выяснить, что в классе case не работает, но это просто абсурдный способ отладки. Трассировка стека, которую я получаю, выглядит следующим образом:
DefaultArg.safeValue$lzycompute(Grater.scala:406)
com.novus.salat.DefaultArg.safeValue(Grater.scala:402)
com.novus.salat.ConcreteGrater.safeDefault(Grater.scala:363)
com.novus.salat.ConcreteGrater$$anonfun$5$$anonfun$apply$3.apply(Grater.scala:262)
com.novus.salat.ConcreteGrater$$anonfun$5$$anonfun$apply$3.apply(Grater.scala:262)
scala.Option.orElse(Option.scala:257)
com.novus.salat.ConcreteGrater$$anonfun$5.apply(Grater.scala:262)
com.novus.salat.ConcreteGrater$$anonfun$5.apply(Grater.scala:247)
scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
scala.collection.immutable.List.foreach(List.scala:318)
scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
scala.collection.AbstractTraversable.map(Traversable.scala:105)
com.novus.salat.ConcreteGrater.asObject(Grater.scala:247)
com.novus.salat.transformers.in.DBObjectToInContext$class.transform0(Injectors.scala:333)
com.novus.salat.transformers.in.DBObjectToInContext$class.transform(Injectors.scala:339)
com.novus.salat.transformers.in.package$$anon$1.transform(Injectors.scala:66)
com.novus.salat.transformers.Transformer$$anonfun$1.apply(Transformer.scala:85)
com.novus.salat.transformers.Transformer$$anonfun$1.apply(Transformer.scala:85)
scala.Option.flatMap(Option.scala:170)
com.novus.salat.transformers.Transformer.transform_$bang(Transformer.scala:85)
com.novus.salat.Field.in_$bang(Field.scala:64)
com.novus.salat.ConcreteGrater$$anonfun$5$$anonfun$apply$2.apply(Grater.scala:262)
com.novus.salat.ConcreteGrater$$anonfun$5$$anonfun$apply$2.apply(Grater.scala:262)
scala.Option.flatMap(Option.scala:170)
com.novus.salat.ConcreteGrater$$anonfun$5.apply(Grater.scala:262)
com.novus.salat.ConcreteGrater$$anonfun$5.apply(Grater.scala:247)
scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
scala.collection.immutable.List.foreach(List.scala:318)
scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
scala.collection.AbstractTraversable.map(Traversable.scala:105)
com.novus.salat.ConcreteGrater.asObject(Grater.scala:247)
com.novus.salat.transformers.in.DBObjectToInContext$class.transform0(Injectors.scala:333)
com.novus.salat.transformers.in.DBObjectToInContext$class.transform(Injectors.scala:338)
com.novus.salat.transformers.in.package$$anon$2.com$novus$salat$transformers$in$TraversableInjector$$super$transform(Injectors.scala:123)
com.novus.salat.transformers.in.TraversableInjector$class.transformElement(Injectors.scala:370)
com.novus.salat.transformers.in.package$$anon$2.transformElement(Injectors.scala:123)
com.novus.salat.transformers.in.TraversableInjector$$anonfun$after$1.apply(Injectors.scala:385)
scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
scala.collection.immutable.List.foreach(List.scala:318)
scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
scala.collection.AbstractTraversable.map(Traversable.scala:105)
com.novus.salat.transformers.in.TraversableInjector$class.after(Injectors.scala:385)
com.novus.salat.transformers.in.package$$anon$2.after(Injectors.scala:123)
com.novus.salat.transformers.Transformer$$anonfun$1.apply(Transformer.scala:85)
com.novus.salat.transformers.Transformer$$anonfun$1.apply(Transformer.scala:85)
scala.Option.flatMap(Option.scala:170)
com.novus.salat.transformers.Transformer.transform_$bang(Transformer.scala:85)
com.novus.salat.Field.in_$bang(Field.scala:64)
com.novus.salat.ConcreteGrater$$anonfun$5$$anonfun$apply$2.apply(Grater.scala:262)
com.novus.salat.ConcreteGrater$$anonfun$5$$anonfun$apply$2.apply(Grater.scala:262)
scala.Option.flatMap(Option.scala:170)
com.novus.salat.ConcreteGrater$$anonfun$5.apply(Grater.scala:262)
com.novus.salat.ConcreteGrater$$anonfun$5.apply(Grater.scala:247)
scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
scala.collection.immutable.List.foreach(List.scala:318)
scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
scala.collection.AbstractTraversable.map(Traversable.scala:105)
com.novus.salat.ConcreteGrater.asObject(Grater.scala:247)
com.sportsnetwork.api.models.MLBPlayByPlay$$anonfun$findByGameId$1.apply(MLBPlayByPlay.scala:38)
com.sportsnetwork.api.models.MLBPlayByPlay$$anonfun$findByGameId$1.apply(MLBPlayByPlay.scala:38)
scala.Option.map(Option.scala:145)
com.sportsnetwork.api.models.MLBPlayByPlay$.findByGameId(MLBPlayByPlay.scala:38)
com.sportsnetwork.api.MLBController$$anonfun$3.apply(MLBController.scala:49)
org.scalatra.ScalatraBase$class.org$scalatra$ScalatraBase$$liftAction(ScalatraBase.scala:250)
org.scalatra.ScalatraBase$$anonfun$invoke$1.apply(ScalatraBase.scala:245)
org.scalatra.ScalatraBase$$anonfun$invoke$1.apply(ScalatraBase.scala:245)
org.scalatra.ApiFormats$class.withRouteMultiParams(ApiFormats.scala:175)
com.sportsnetwork.api.MLBController.withRouteMultiParams(MLBController.scala:20)
org.scalatra.ScalatraBase$class.invoke(ScalatraBase.scala:244)
com.sportsnetwork.api.MLBController.org$scalatra$json$JsonSupport$$super$invoke(MLBController.scala:20)
org.scalatra.json.JsonSupport$$anonfun$invoke$1.apply(JsonSupport.scala:65)
org.scalatra.json.JsonSupport$$anonfun$invoke$1.apply(JsonSupport.scala:57)
org.scalatra.ApiFormats$class.withRouteMultiParams(ApiFormats.scala:175)
com.sportsnetwork.api.MLBController.withRouteMultiParams(MLBController.scala:20)
org.scalatra.json.JsonSupport$class.invoke(JsonSupport.scala:57)
com.sportsnetwork.api.MLBController.invoke(MLBController.scala:20)
org.scalatra.ScalatraBase$$anonfun$runRoutes$1$$anonfun$apply$7.apply(ScalatraBase.scala:220)
org.scalatra.ScalatraBase$$anonfun$runRoutes$1$$anonfun$apply$7.apply(ScalatraBase.scala:218)
scala.Option.flatMap(Option.scala:170)
org.scalatra.ScalatraBase$$anonfun$runRoutes$1.apply(ScalatraBase.scala:218)
org.scalatra.ScalatraBase$$anonfun$runRoutes$1.apply(ScalatraBase.scala:217)
scala.collection.immutable.Stream.flatMap(Stream.scala:442)
org.scalatra.ScalatraBase$class.runRoutes(ScalatraBase.scala:217)
org.scalatra.ScalatraServlet.runRoutes(ScalatraServlet.scala:49)
org.scalatra.ScalatraBase$class.runActions$1(ScalatraBase.scala:163)
org.scalatra.ScalatraBase$$anonfun$executeRoutes$1.apply$mcV$sp(ScalatraBase.scala:175)
org.scalatra.ScalatraBase$$anonfun$executeRoutes$1.apply(ScalatraBase.scala:175)
org.scalatra.ScalatraBase$$anonfun$executeRoutes$1.apply(ScalatraBase.scala:175)
org.scalatra.ScalatraBase$class.org$scalatra$ScalatraBase$$cradleHalt(ScalatraBase.scala:190)
org.scalatra.ScalatraBase$class.executeRoutes(ScalatraBase.scala:175)
org.scalatra.ScalatraServlet.executeRoutes(ScalatraServlet.scala:49)
org.scalatra.ScalatraBase$$anonfun$handle$1.apply$mcV$sp(ScalatraBase.scala:113)
org.scalatra.ScalatraBase$$anonfun$handle$1.apply(ScalatraBase.scala:113)
org.scalatra.ScalatraBase$$anonfun$handle$1.apply(ScalatraBase.scala:113)
scala.util.DynamicVariable.withValue(DynamicVariable.scala:57)
org.scalatra.DynamicScope$class.withResponse(DynamicScope.scala:80)
org.scalatra.ScalatraServlet.withResponse(ScalatraServlet.scala:49)
org.scalatra.DynamicScope$$anonfun$withRequestResponse$1.apply(DynamicScope.scala:60)
scala.util.DynamicVariable.withValue(DynamicVariable.scala:57)
org.scalatra.DynamicScope$class.withRequest(DynamicScope.scala:71)
org.scalatra.ScalatraServlet.withRequest(ScalatraServlet.scala:49)
org.scalatra.DynamicScope$class.withRequestResponse(DynamicScope.scala:59)
org.scalatra.ScalatraServlet.withRequestResponse(ScalatraServlet.scala:49)
org.scalatra.ScalatraBase$class.handle(ScalatraBase.scala:111)
org.scalatra.ScalatraServlet.org$scalatra$servlet$ServletBase$$super$handle(ScalatraServlet.scala:49)
org.scalatra.servlet.ServletBase$class.handle(ServletBase.scala:43)
com.sportsnetwork.api.MLBController.org$scalatra$scalate$ScalateSupport$$super$handle(MLBController.scala:20)
org.scalatra.scalate.ScalateSupport$class.handle(ScalateSupport.scala:122)
com.sportsnetwork.api.MLBController.org$scalatra$CorsSupport$$super$handle(MLBController.scala:20)
org.scalatra.CorsSupport$$anonfun$handle$1.apply$mcV$sp(CORSSupport.scala:202)
org.scalatra.CorsSupport$$anonfun$handle$1.apply(CORSSupport.scala:190)
org.scalatra.CorsSupport$$anonfun$handle$1.apply(CORSSupport.scala:190)
scala.util.DynamicVariable.withValue(DynamicVariable.scala:57)
org.scalatra.DynamicScope$class.withResponse(DynamicScope.scala:80)
org.scalatra.ScalatraServlet.withResponse(ScalatraServlet.scala:49)
org.scalatra.DynamicScope$$anonfun$withRequestResponse$1.apply(DynamicScope.scala:60)
scala.util.DynamicVariable.withValue(DynamicVariable.scala:57)
org.scalatra.DynamicScope$class.withRequest(DynamicScope.scala:71)
org.scalatra.ScalatraServlet.withRequest(ScalatraServlet.scala:49)
org.scalatra.DynamicScope$class.withRequestResponse(DynamicScope.scala:59)
org.scalatra.ScalatraServlet.withRequestResponse(ScalatraServlet.scala:49)
org.scalatra.CorsSupport$class.handle(CORSSupport.scala:188)
com.sportsnetwork.api.MLBController.handle(MLBController.scala:20)
org.scalatra.ScalatraServlet.service(ScalatraServlet.scala:54)
javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:669)
org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:455)
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137)
org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:560)
org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231)
org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1072)
org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:382)
org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193)
org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1006)
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135)
org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:255)
org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
org.eclipse.jetty.server.Server.handle(Server.java:361)
org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:485)
org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:926)
org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:988)
org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:635)
org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235)
org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:82)
org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:628)
org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:52)
org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
java.lang.Thread.run(Thread.java:701)
Примером моих классов case является что-то вроде следующего:
case class MLBPlayByPlay(datetime: DateTime, gamedate: DateTime, game_id: Int, home_team_id: Int, away_team_id: Int,
home_team_score: Int, away_team_score: Int, sport: String, season_year: Int, season_type: String, location: String, status: String,
home_team_abbr: String, away_team_abbr: String, hp_umpire: String, fb_umpire: String, sb_umpire: String, tb_umpire: String,
play_by_play_seqs: Seq[MLBPlayByPlaySeq], created_at: Option[Date] = None, updated_at: Date)
case class MLBPlayByPlaySeq(batter: Option[MLBBatter] = None, next_batter: Option[MLBBatter] = None, pitchers: Option[Seq[MLBPitcher]] = None, pitches: Option[Seq[MLBPitches]] = None, fielders: Option[Seq[MLBFielder]] = None,
seq_id: Int, inning: Option[Int] = None, outs: Option[Int] = None, no_play: Option[Int] = None, home_team_score: Int, away_team_score: Int)
case class MLBBatter(batter_id: Int, team_id: Int, first_name: String, last_name: String, action: Option[String] = None, out: Option[Int] = None, adv: Option[Int] = None,
to_base: Option[Int] = None, scored: Option[Int] = None, bats: Option[String] = None, appear: Option[Int] = None, pos: Option[String] = None,
pitcher_id: Option[Int] = None, pthrows: Option[String] = None, error_ids: Option[String] = None, location: Option[String] = None, traj: Option[String] = None,
field: Option[String] = None, coords: Option[String] = None, ab: Option[Int] = None, gndout: Option[Int] = None)
case class MLBPitcher(pitcher_id: Int, team_id: Int, first_name: String, last_name: String, inning: Int, outs: Int, throws: String, bbats: String, ab: Int, bf: Int)
case class MLBFielder(fielder_id: Int, team_id: Int, first_name: String, last_name: String, pos: String, assist: Int, put_out: Int)
case class MLBPitches(count: Int, total_pitches: Int, balls: Int, strikes: Int, value: String, batter_id: Int, pitcher_id: Int, runner_1_id: Int, runner_2_id: Int,
runnerMLBPlayByPlay
id: Int, final_pitch: String)
Я начал отлаживать это, комментируя поле за полем, начиная с класса case MLBPlayByPlay
, и API нормально реагировал. В конце концов, когда я раскомментировал play_by_play_seqs: Seq[MLBPlayByPlaySeq]
, все взорвалось. Теперь мне нужно перейти к классу case MLBPlayByPlaySeq, который включает свои собственные типы классов case, и начать комментировать там один за другим... наверняка есть более простой способ отладки Salat? Вот как я использую терку:
val res = MLBPlayByPlayDAO.findByGameId(gameId)
res.map(grater[MLBPlayByPlay].asObject(_))
Фактическое исключение:
k.api.MLBController - exception occured: com.novus.salat.DefaultArg.safeValue$lzycompute(Grater.scala:406)
Тема будет обновлена вскоре после того, как я удалю Option[Seq[T]]
из полей.
** ОБНОВЛЕНИЕ 2 **
После удаления всех Option[Seq[T]]
из моих классов случаев я сузил проблему, когда next_batter
является пустым хэшем в моей базе данных для класса случаев MLBPlayByPlaySeqs
. Я изменил определение с next_batter: Option[MLBBatter] = None
(это приведет к взрыву пустого хэша) на next_batter: Map[String, Any] = Map.empty
, и это работает. Проблема здесь в том, что:
- Я потерял безопасность типов, используя карту
- Я больше не могу вручную выбирать, какие атрибуты я хочу перечислить в моем сериализованном выводе JSON. Такие вещи, как created_at и updated_at для встроенного хэша, отображаются в моем JSON, и это вещи, которые я явно пропустил в классе case, которые я не могу сделать, изменив
next_batter
на Map вместо класса case.