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
22 fields limit in Scala 2.11 + Play Framework 2.3 Case classes and functions
提问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:
这是不可能的,开箱即用,原因如下:
First, as gourlaysamapointed it out, play-json library used scala macro to avoid bolierplate code, and the current coderelies of the
unapplyandapplymethods to retrieve fields. This explains the first error message in your question.Secondly play-json library relies of a functional librarywhich currently works only with a fixed number of parameterscorresponding to previous case class fields arity limit. This explains the second error message in your question.
首先,正如gourlaysama所指出的,play-json库使用scala宏来避免样板代码,当前代码依赖于
unapply和apply方法来检索字段。这解释了您问题中的第一条错误消息。其次,play-json 库依赖于一个功能库,该库目前仅使用与先前案例类字段数量限制相对应的固定数量的参数。这解释了您问题中的第二条错误消息。
However it is possible to bypass the second point by either:
但是,可以通过以下任一方式绕过第二点:
using shapelessAutomatic Typeclass Derivationfeature. Naveen Gattuhas written an excellent gistdoing exaclty so.
overridding default functiunal builder
使用无形的自动类型类派生功能。Naveen Gattu写了一个很好的要旨,这样做确实如此。
覆盖默认功能构建器
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

