Scala 2.11 + Play Framework 2.3 Case 类和函数中的 22 个字段限制

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23571677/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 06:15:18  来源:igfitidea点击:

22 fields limit in Scala 2.11 + Play Framework 2.3 Case classes and functions

scalaplayframework-2.3

提问by luis

Scala 2.11 is out and the 22 fields limit for case classes seems to be fixed (Scala Issue, Release Notes).

Scala 2.11 已发布,案例类的 22 个字段限制似乎已修复(Scala 问题发行说明)。

This has been an issue for me for a while because I use case classes to model database entities that have more than 22 fields in Play + Postgres Async. My solution in Scala 2.10 was to break the models into multiple case classes, but I find this solution hard to maintain and extend, and I was hoping I could implement something as described below after switching to Play 2.3.0-RC1 + Scala 2.11.0:

这对我来说已经有一段时间了,因为我使用案例类来建模在 Play + Postgres Async中有超过 22 个字段的数据库实体。我在 Scala 2.10 中的解决方案是将模型分解为多个案例类,但我发现这个解决方案难以维护和扩展,我希望在切换到 Play 2.3.0-RC1 + Scala 2.11 后我可以实现如下所述的内容。 0:

package entities

case class MyDbEntity(
  id: String,
  field1: String,
  field2: Boolean,
  field3: String,
  field4: String,
  field5: String,
  field6: String,
  field7: String,
  field8: String,
  field9: String,
  field10: String,
  field11: String,
  field12: String,
  field13: String,
  field14: String,
  field15: String,
  field16: String,
  field17: String,
  field18: String,
  field19: String,
  field20: String,
  field21: String,
  field22: String,
  field23: String,
) 

object MyDbEntity {
  import play.api.libs.json.Json
  import play.api.data._
  import play.api.data.Forms._

  implicit val entityReads = Json.reads[MyDbEntity]
  implicit val entityWrites = Json.writes[MyDbEntity]
}

The code above fails to compile with the following message for both the "Reads" and the "Writes":

上面的代码无法编译“读取”和“写入”,并显示以下消息:

No unapply function found

No unapply function found

Updating the "Reads" and "Writes" to:

将“读取”和“写入”更新为:

  implicit val entityReads: Reads[MyDbEntity] = (
    (__ \ "id").read[Long] and
    (__ \ "field_1").read[String]
    ........
  )(MyDbEntity.apply _)  

  implicit val postWrites: Writes[MyDbEntity] = (
    (__ \ "id").write[Long] and
    (__ \ "user").write[String]
    ........
  )(unlift(MyDbEntity.unapply))

Also doesn't work:

也不起作用:

  implementation restricts functions to 22 parameters

  value unapply is not a member of object models.MyDbEntity

My understanding is that Scala 2.11 still has some limitations on functions and that something like what I described above is not possible yet. This seems weird to me as I don't see the benefit of lifting the restrictions on case classes if one it's major users cases is still not supported, so I'm wondering if I'm missing something.

我的理解是 Scala 2.11 在函数方面仍然有一些限制,并且像我上面描述的那样的东西尚不可能。这对我来说似乎很奇怪,因为如果仍然不支持主要用户案例,我看不到取消对案例类的限制的好处,所以我想知道我是否遗漏了什么。

Pointers to issues or implementation details are more than welcome! Thanks!

非常欢迎指向问题或实现细节的指针!谢谢!

采纳答案by mchv

This is not possible, out of the box, for several reasons:

这是不可能的,开箱即用,原因如下:

However it is possible to bypass the second point by either:

但是,可以通过以下任一方式绕过第二点:

First, creating the missing FunctionalBuilder:

首先,创建缺少的FunctionalBuilder

class CustomFunctionalBuilder[M[_]](canBuild: FunctionalCanBuild[M]) extends FunctionalBuilder {

    class CustomCanBuild22[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22](m1: M[A1 ~ A2 ~ A3 ~ A4 ~ A5 ~ A6 ~ A7 ~ A8 ~ A9 ~ A10 ~ A11 ~ A12 ~ A13 ~ A14 ~ A15 ~ A16 ~ A17 ~ A18 ~ A19 ~ A20 ~ A21], m2: M[A22]) {
def ~[A23](m3: M[A23]) = new CustomCanBuild23[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23](canBuild(m1, m2), m3)

  def and[A23](m3: M[A23]) = this.~(m3)

  def apply[B](f: (A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22) => B)(implicit fu: Functor[M]): M[B] =
  fu.fmap[A1 ~ A2 ~ A3 ~ A4 ~ A5 ~ A6 ~ A7 ~ A8 ~ A9 ~ A10 ~ A11 ~ A12 ~ A13 ~ A14 ~ A15 ~ A16 ~ A17 ~ A18 ~ A19 ~ A20 ~ A21 ~ A22, B](canBuild(m1, m2), { case a1 ~ a2 ~ a3 ~ a4 ~ a5 ~ a6 ~ a7 ~ a8 ~ a9 ~ a10 ~ a11 ~ a12 ~ a13 ~ a14 ~ a15 ~ a16 ~ a17 ~ a18 ~ a19 ~ a20 ~ a21 ~ a22 => f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22) })

  def apply[B](f: B => (A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22))(implicit fu: ContravariantFunctor[M]): M[B] =
  fu.contramap(canBuild(m1, m2), (b: B) => { val (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22) = f(b); new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(a1, a2), a3), a4), a5), a6), a7), a8), a9), a10), a11), a12), a13), a14), a15), a16), a17), a18), a19), a20), a21), a22) })

  def apply[B](f1: (A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22) => B, f2: B => (A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22))(implicit fu: InvariantFunctor[M]): M[B] =
  fu.inmap[A1 ~ A2 ~ A3 ~ A4 ~ A5 ~ A6 ~ A7 ~ A8 ~ A9 ~ A10 ~ A11 ~ A12 ~ A13 ~ A14 ~ A15 ~ A16 ~ A17 ~ A18 ~ A19 ~ A20 ~ A21 ~ A22, B](
    canBuild(m1, m2), { case a1 ~ a2 ~ a3 ~ a4 ~ a5 ~ a6 ~ a7 ~ a8 ~ a9 ~ a10 ~ a11 ~ a12 ~ a13 ~ a14 ~ a15 ~ a16 ~ a17 ~ a18 ~ a19 ~ a20 ~ a21 ~ a22 => f1(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22) },
    (b: B) => { val (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22) = f2(b); new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(new ~(a1, a2), a3), a4), a5), a6), a7), a8), a9), a10), a11), a12), a13), a14), a15), a16), a17), a18), a19), a20), a21), a22) }
  )

  def join[A >: A1](implicit witness1: <:<[A, A1], witness2: <:<[A, A2], witness3: <:<[A, A3], witness4: <:<[A, A4], witness5: <:<[A, A5], witness6: <:<[A, A6], witness7: <:<[A, A7], witness8: <:<[A, A8], witness9: <:<[A, A9], witness10: <:<[A, A10], witness11: <:<[A, A11], witness12: <:<[A, A12], witness13: <:<[A, A13], witness14: <:<[A, A14], witness15: <:<[A, A15], witness16: <:<[A, A16], witness17: <:<[A, A17], witness18: <:<[A, A18], witness19: <:<[A, A19], witness20: <:<[A, A20], witness21: <:<[A, A21], witness22: <:<[A, A22], fu: ContravariantFunctor[M]): M[A] =
  apply[A]((a: A) => (a: A1, a: A2, a: A3, a: A4, a: A5, a: A6, a: A7, a: A8, a: A9, a: A10, a: A11, a: A12, a: A13, a: A14, a: A15, a: A16, a: A17, a: A18, a: A19, a: A20, a: A21, a: A22))(fu)

  def reduce[A >: A1, B](implicit witness1: <:<[A1, A], witness2: <:<[A2, A], witness3: <:<[A3, A], witness4: <:<[A4, A], witness5: <:<[A5, A], witness6: <:<[A6, A], witness7: <:<[A7, A], witness8: <:<[A8, A], witness9: <:<[A9, A], witness10: <:<[A10, A], witness11: <:<[A11, A], witness12: <:<[A12, A], witness13: <:<[A13, A], witness14: <:<[A14, A], witness15: <:<[A15, A], witness16: <:<[A16, A], witness17: <:<[A17, A], witness18: <:<[A18, A], witness19: <:<[A19, A], witness20: <:<[A20, A], witness21: <:<[A21, A], witness22: <:<[A22, A], fu: Functor[M], reducer: Reducer[A, B]): M[B] =
  apply[B]((a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8, a9: A9, a10: A10, a11: A11, a12: A12, a13: A13, a14: A14, a15: A15, a16: A16, a17: A17, a18: A18, a19: A19, a20: A20, a21: A21, a22: A22) =>  reducer.append(reducer.append(reducer.append(reducer.append(reducer.append(reducer.append(reducer.append(reducer.append(reducer.append(reducer.append(reducer.append(reducer.append(reducer.append(reducer.append(reducer.append(reducer.append(reducer.append(reducer.append(reducer.append(reducer.append(reducer.append(reducer.unit(a1: A), a2: A), a3: A), a4: A), a5: A), a6: A), a7: A), a8: A), a9: A), a10: A), a11: A), a12: A), a13: A), a14: A), a15: A), a16: A), a17: A), a18: A), a19: A), a20: A), a21: A), a22: A))(fu)

  def tupled(implicit v: VariantExtractor[M]): M[(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22)] =
  v match {
    case FunctorExtractor(fu) => apply { (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8, a9: A9, a10: A10, a11: A11, a12: A12, a13: A13, a14: A14, a15: A15, a16: A16, a17: A17, a18: A18, a19: A19, a20: A20, a21: A21, a22: A22) => (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22) }(fu)
    case ContravariantFunctorExtractor(fu) => apply[(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22)] { (a: (A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22)) => (a._1, a._2, a._3, a._4, a._5, a._6, a._7, a._8, a._9, a._10, a._11, a._12, a._13, a._14, a._15, a._16, a._17, a._18, a._19, a._20, a._21, a._22) }(fu)
    case InvariantFunctorExtractor(fu) => apply[(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22)]({ (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8, a9: A9, a10: A10, a11: A11, a12: A12, a13: A13, a14: A14, a15: A15, a16: A16, a17: A17, a18: A18, a19: A19, a20: A20, a21: A21, a22: A22) => (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22) }, { (a: (A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22)) => (a._1, a._2, a._3, a._4, a._5, a._6, a._7, a._8, a._9, a._10, a._11, a._12, a._13, a._14, a._15, a._16, a._17, a._18, a._19, a._20, a._21, a._22) })(fu)
    }

  }

  class CustomCanBuild23[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23](m1: M[A1 ~ A2 ~ A3 ~ A4 ~ A5 ~ A6 ~ A7 ~ A8 ~ A9 ~ A10 ~ A11 ~ A12 ~ A13 ~ A14 ~ A15 ~ A16 ~ A17 ~ A18 ~ A19 ~ A20 ~ A21 ~ A22], m2: M[A23]) {
  }

}

and then by providing your own FunctionalBuilderOpsinstance:

然后通过提供您自己的FunctionalBuilderOps实例:

implicit def customToFunctionalBuilderOps[M[_], A](a: M[A])(implicit fcb: FunctionalCanBuild[M]) = new CustomFunctionalBuilderOps[M, A](a)(fcb)

Finally, regarding the first point, I have sent a pull requestto try to simplify the current implementation.

最后,关于第一点,我已经发送了一个pull request来尝试简化当前的实现。

回答by Zim-Zam O'Pootertoot

We were also breaking our models into multiple case classes, but this was quickly becoming unmanageable. We use Slickas our object relational mapper, and Slick 2.0 comes with a code generatorthat we use to generate classes (which come with apply methods and copy constructors to mimic case classes) along with methods to instantiate models from Json (we do not automatically generate methods to convert models into Json because we have too many special cases to deal with). Using the Slick code generator does not require you to use Slick as your object relational mapper.

我们还将我们的模型分解为多个案例类,但这很快变得难以管理。我们使用Slick作为我们的对象关系映射器,Slick 2.0 带有一个代码生成器,我们用来生成类(带有应用方法和复制构造函数来模拟案例类)以及从 Json 实例化模型的方法(我们不会自动生成方法将模型转换为 Json,因为我们有太多特殊情况需要处理)。使用 Slick 代码生成器不需要您使用 Slick 作为对象关系映射器。

This is part of the input to the code generator - this method takes a JsObject and uses it to either instantiate a new model or update an existing model.

这是代码生成器输入的一部分 - 此方法采用 JsObject 并使用它来实例化新模型或更新现有模型。

private def getItem(original: Option[${name}], json: JsObject, trackingData: TrackingData)(implicit session: scala.slick.session.Session): Try[${name}] = {
  preProcess("$name", columnSet, json, trackingData).flatMap(updatedJson => {
    ${indent(indent(indent(entityColumnsSansId.map(c => s"""val ${c.name}_Parsed = parseJsonField[${c.exposedType}](original.map(_.${c.name}), "${c.name}", updatedJson, "${c.exposedType}")""").mkString("\n"))))}
    val errs = Seq(${indent(indent(indent(indent(entityColumnsSansId.map(c => s"${c.name}_Parsed.map(_ => ())").mkString(", ")))))}).condenseUnit
    for {
      _ <- errs
      ${indent(indent(indent(indent(entityColumnsSansId.map(c => s"${c.name}_Val <- ${c.name}_Parsed").mkString("\n")))))}
    } yield {
      original.map(_.copy(${entityColumnsSansId.map(c => s"${c.name} = ${c.name}_Val").mkString(", ")}))
        .getOrElse(${name}.apply(id = None, ${entityColumnsSansId.map(c => s"${c.name} = ${c.name}_Val").mkString(", ")}))
    }
  })
}

For example, with our ActivityLog model this produces the following code. If "original" is None then this is being called from a "createFromJson" method and we instantiate a new model; if "original" is Some(activityLog) then this is being called from an "updateFromJson" method and we update the existing model. The "condenseUnit" method being called on the "val errs = ..." line takes a Seq[Try[Unit]] and produces a Try[Unit]; if the Seq has any errors then the Try[Unit] concatenates the exception messages. The parseJsonField and parseField methods are not generated - they're just referenced from the generated code.

例如,对于我们的 ActivityLog 模型,这会生成以下代码。如果“original”是None,那么这是从“createFromJson”方法调用的,我们实例化一个新模型;如果“原始”是 Some(activityLog) 则它是从“updateFromJson”方法调用的,我们更新现有模型。在“val errs = ...”行上调用的“condenseUnit”方法接受一个 Seq[Try[Unit]] 并产生一个 Try[Unit];如果 Seq 有任何错误,则 Try[Unit] 连接异常消息。parseJsonField 和 parseField 方法没有生成——它们只是从生成的代码中引用。

private def parseField[T](name: String, json: JsObject, tpe: String)(implicit r: Reads[T]): Try[T] = {
  Try((json \ name).as[T]).recoverWith {
    case e: Exception => Failure(new IllegalArgumentException("Failed to parse " + Json.stringify(json \ name) + " as " + name + " : " + tpe))
  }
}

def parseJsonField[T](default: Option[T], name: String, json: JsObject, tpe: String)(implicit r: Reads[T]): Try[T] = {
  default match {
    case Some(t) => if(json.keys.contains(name)) parseField(name, json, tpe)(r) else Try(t)
    case _ => parseField(name, json, tpe)(r)
  }
}

private def getItem(original: Option[ActivityLog], json: JsObject, trackingData: TrackingData)(implicit session: scala.slick.session.Session): Try[ActivityLog] = {
  preProcess("ActivityLog", columnSet, json, trackingData).flatMap(updatedJson => {
    val user_id_Parsed = parseJsonField[Option[Int]](original.map(_.user_id), "user_id", updatedJson, "Option[Int]")
    val user_name_Parsed = parseJsonField[Option[String]](original.map(_.user_name), "user_name", updatedJson, "Option[String]")
    val item_id_Parsed = parseJsonField[Option[String]](original.map(_.item_id), "item_id", updatedJson, "Option[String]")
    val item_item_type_Parsed = parseJsonField[Option[String]](original.map(_.item_item_type), "item_item_type", updatedJson, "Option[String]")
    val item_name_Parsed = parseJsonField[Option[String]](original.map(_.item_name), "item_name", updatedJson, "Option[String]")
    val modified_Parsed = parseJsonField[Option[String]](original.map(_.modified), "modified", updatedJson, "Option[String]")
    val action_name_Parsed = parseJsonField[Option[String]](original.map(_.action_name), "action_name", updatedJson, "Option[String]")
    val remote_ip_Parsed = parseJsonField[Option[String]](original.map(_.remote_ip), "remote_ip", updatedJson, "Option[String]")
    val item_key_Parsed = parseJsonField[Option[String]](original.map(_.item_key), "item_key", updatedJson, "Option[String]")
    val created_at_Parsed = parseJsonField[Option[java.sql.Timestamp]](original.map(_.created_at), "created_at", updatedJson, "Option[java.sql.Timestamp]")
    val as_of_date_Parsed = parseJsonField[Option[java.sql.Timestamp]](original.map(_.as_of_date), "as_of_date", updatedJson, "Option[java.sql.Timestamp]")
    val errs = Seq(user_id_Parsed.map(_ => ()), user_name_Parsed.map(_ => ()), item_id_Parsed.map(_ => ()), item_item_type_Parsed.map(_ => ()), item_name_Parsed.map(_ => ()), modified_Parsed.map(_ => ()), action_name_Parsed.map(_ => ()), remote_ip_Parsed.map(_ => ()), item_key_Parsed.map(_ => ()), created_at_Parsed.map(_ => ()), as_of_date_Parsed.map(_ => ())).condenseUnit
    for {
      _ <- errs
      user_id_Val <- user_id_Parsed
      user_name_Val <- user_name_Parsed
      item_id_Val <- item_id_Parsed
      item_item_type_Val <- item_item_type_Parsed
      item_name_Val <- item_name_Parsed
      modified_Val <- modified_Parsed
      action_name_Val <- action_name_Parsed
      remote_ip_Val <- remote_ip_Parsed
      item_key_Val <- item_key_Parsed
      created_at_Val <- created_at_Parsed
      as_of_date_Val <- as_of_date_Parsed
    } yield {
      original.map(_.copy(user_id = user_id_Val, user_name = user_name_Val, item_id = item_id_Val, item_item_type = item_item_type_Val, item_name = item_name_Val, modified = modified_Val, action_name = action_name_Val, remote_ip = remote_ip_Val, item_key = item_key_Val, created_at = created_at_Val, as_of_date = as_of_date_Val))
        .getOrElse(ActivityLog.apply(id = None, user_id = user_id_Val, user_name = user_name_Val, item_id = item_id_Val, item_item_type = item_item_type_Val, item_name = item_name_Val, modified = modified_Val, action_name = action_name_Val, remote_ip = remote_ip_Val, item_key = item_key_Val, created_at = created_at_Val, as_of_date = as_of_date_Val))
    }
  })
}

回答by CharlieQ

You can use Hymanson's Scala module. Play's json feature is built upon Hymanson scala . I don't know why they put a 22 field limit here while Hymanson supports more than 22 fields. It may make sense that a function call can never use more than 22 parameters, but we can have hundreds of columns inside a DB entity, so this restriction here is ridiculous and makes Play a less productive toy. check this out:

您可以使用 Hymanson 的 Scala 模块。Play 的 json 功能建立在 Hymanson scala 之上。我不知道为什么他们在这里设置了 22 个字段的限制,而 Hymanson 支持超过 22 个字段。一个函数调用永远不能使用超过 22 个参数可能是有道理的,但我们可以在一个 DB 实体中有数百个列,所以这里的这个限制是荒谬的,并且使 Play 成为一个效率较低的玩具。看一下这个:

import com.fasterxml.Hymanson.databind.ObjectMapper
import com.fasterxml.Hymanson.module.scala.experimental.ScalaObjectMapper
import com.fasterxml.Hymanson.module.scala.DefaultScalaModule

object HymansonUtil extends App {
  val mapper = new ObjectMapper with ScalaObjectMapper
  mapper.registerModule(DefaultScalaModule)


  val t23 = T23("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w")

  println(mapper.writeValueAsString(t23))
 }
case class T23(f1:String,f2:String,f3:String,f4:String,f5:String,f6:String,f7:String,
    f8:String,f9:String,f10:String,f11:String,f12:String,f13:String,f14:String,f15:String,
    f16:String,f17:String,f18:String,f19:String,f20:String,f21:String,f22:String,f23:String)

回答by Venu A Positive

cases where case classes might not work; one of these cases is that the case classes cannot take more than 22 fields. Another case can be that you do not know about schema beforehand. In this approach, the data is loaded as an RDD of the row objects. Schema is created separately using the StructType and StructField objects, which represent a table and a field respectively. Schema is applied to the row RDD to create DataFrame in Spark.

案例类可能不起作用的情况;其中一种情况是案例类不能超过 22 个字段。另一种情况可能是您事先不了解架构。在这种方法中,数据作为行对象的 RDD 加载。Schema 是使用 StructType 和 StructField 对象分别创建的,它们分别代表一个表和一个字段。Schema 应用于行 RDD 以在 Spark 中创建DataFrame

回答by Shanness

It seems this handles it all nicely.

这似乎很好地处理了这一切。

+22 field case class formatter and more for play-jsonhttps://github.com/xdotai/play-json-extensions

+22 字段案例类格式化程序以及更多用于 play-json https://github.com/xdotai/play-json-extensions

Supports Scala 2.11.x, 2.12.x, and 2.13.x and play 2.3, 2.4, 2.5 and 2.7

支持 Scala 2.11.x、2.12.x 和 2.13.x 并播放 2.3、2.4、2.5 和 2.7

And is referenced in the play-json issueas the preferred solution (but not yet merged)

并且在play-json 问题中被引用为首选解决方案(但尚未合并)

回答by Tvaroh

I tried Shapeless "Automatic Typeclass Derivation" based solution proposed in another answer, and it didn't work for our models - was throwing StackOverflow exceptions (case class with ~30 fields and 4 nested collections of case classes with 4-10 fields).

我尝试了另一个答案中提出的基于 Shapeless“Automatic Typeclass Derivation”的解决方案,但它对我们的模型不起作用 - 抛出 StackOverflow 异常(具有约 30 个字段的案例类和具有 4-10 个字段的案例类的 4 个嵌套集合)。

So, we've adopted thissolution and it worked flawlessly. Confirmed that by writing ScalaCheck test. Notice, that it requires Play Json 2.4.

所以,我们采用了这个解决方案,它完美地工作。通过编写 ScalaCheck 测试确认了这一点。请注意,它需要 Play Json 2.4。

回答by Kenji Yoshida

I'm making a library. please try this https://github.com/xuwei-k/play-twenty-three

我正在制作一个图书馆。请试试这个https://github.com/xuwei-k/play-twenty-three