如何在Scala中为schema.org建模?

如何在Scala中为schema.org建模?,scala,data-modeling,multiple-inheritance,schema.org,Scala,Data Modeling,Multiple Inheritance,Schema.org,是标记词汇表(用于web),并根据属性定义了许多类型(无方法)。我目前正试图在Scala中将该模式的部分建模为内部模型类,与面向文档的数据库(MongoDB)和web框架结合使用 从的定义中可以看出,schema.org使用多重继承还包括来自“Place”类型的属性。所以我的问题是:您将如何在Scala中建模这样的模式? 到目前为止,我已经提出了两个解决方案。第一种方法使用常规类来建模单个继承树,并使用特征来混合这些附加属性 trait ThingA { var name: String =

是标记词汇表(用于web),并根据属性定义了许多类型(无方法)。我目前正试图在Scala中将该模式的部分建模为内部模型类,与面向文档的数据库(MongoDB)和web框架结合使用

从的定义中可以看出,schema.org使用多重继承还包括来自“Place”类型的属性。所以我的问题是:您将如何在Scala中建模这样的模式?

到目前为止,我已经提出了两个解决方案。第一种方法使用常规类来建模单个继承树,并使用特征来混合这些附加属性

trait ThingA {
  var name: String = ""
  var url: String = ""
}

trait OrganizationA {
  var email: String = ""
}

trait PlaceA {
  var x: String = ""
  var y: String = ""
}

trait LocalBusinessA {
  var priceRange: String = ""
}

class OrganizationClassA extends ThingA with OrganizationA {}

class LocalBusinessClassA extends OrganizationClassA with PlaceA with LocalBusinessA {}
第二个版本尝试使用case类。但是,由于case类继承已被弃用,我无法如此轻松地对主层次结构建模

trait ThingB {
  val name: String
}

trait OrganizationB {
  val email: String
}

trait PlaceB {
  val x: String
  val y: String
}

trait LocalBusinessB {
  val priceRange: String
}

case class OrganizationClassB(val name: String, val email: String) extends ThingB with OrganizationB

case class LocalBusinessClassB(val name: String, val email: String, val x: String, val y: String, val priceRange: String) extends ThingB with OrganizationB with PlaceB with LocalBusinessB
是否有更好的方法对此进行建模?我可以使用与

case class LocalBusinessClassC(val thing:ThingClass, val place: PlaceClass, ...)

当然,当需要“地点”时,例如当我试图在Google Maps上渲染某些内容时,不能使用LocalBusiness。

最适合您的方式在很大程度上取决于您希望如何将对象映射到基础数据存储

考虑到多重继承的需要,可能值得考虑的方法是只使用特征。这使您可以使用最少的代码重复或样板文件实现多重继承

trait Thing {
  val name: String               // required
  val url: Option[String] = None // reasonable default
}

trait Organization extends Thing {
  val email: Option[String] = None
}

trait Place extends Thing {
  val x: String
  val y: String
}

trait LocalBusiness extends Organization with Place {
  val priceRange: String
}
请注意,
组织
扩展了
事物
放置
,就像在中一样

要实例化它们,您需要创建匿名内部类来指定所有属性的值

object UseIt extends App {
  val home = new Place {
    val name = "Home"
    val x = "-86.586104"
    val y = "34.730369"
  }

  val oz = new Place {
    val name = "Oz"
    val x = "151.206890"
    val y = "-33.873651"
  }

  val paulis = new LocalBusiness {
    val name = "Pauli's"
    override val url = "http://www.paulisbarandgrill.com/"
    val x = "-86.713660"
    val y = "34.755092"
    val priceRange = "$$$"
  }

}
如果任何字段具有合理的默认值,则可以在trait中指定默认值

我将没有值的字段保留为空字符串,但创建类型为
Option[String]
的可选字段可能更有意义,以便更好地指示它们的值未设置。您喜欢使用
选项
,因此我正在使用
选项

这种方法的缺点是,编译器会在您实例化其中一个特征的每个位置生成一个匿名内部类。这可能会使
.class
文件激增。但更重要的是,这意味着同一特质的不同实例将具有不同的类型

编辑:

关于如何使用它从数据库加载对象,这在很大程度上取决于访问数据库的方式。如果使用对象映射器,则需要按照映射器期望的方式构造模型对象。如果这种技巧在你的对象映射器上有效,我会感到惊讶

如果您正在编写自己的数据访问层,那么您可以简单地使用or模式进行数据访问,将构建匿名内部类的逻辑放在其中

这只是构造这些对象的一种方法。这甚至不是最好的方法,但它证明了这一点

trait Database {
  // treats objects as simple key/value pairs
  def findObject(id: String): Option[Map[String, String]]
}

class ThingRepo(db: Database) {
  def findThing(id: String): Option[Thing] = {
    // Note that in this way, malformed objects (i.e. missing name) simply
    // return None. Logging or other responses for malformed objects is left
    // as an exercise :-)
    for {
      fields <- db.findObject(id) // load object from database
      name <- field.get("name")   // extract required field
    } yield {
      new Thing {
        val name = name
        val url = field.get("url")
      }
    }
  }
}
trait数据库{
//将对象视为简单的键/值对
def findObject(id:String):选项[Map[String,String]]
}
类ThingRepo(db:数据库){
def findThing(id:String):选项[Thing]={
//请注意,通过这种方式,格式错误的对象(即缺少名称)只需
//返回None。保留对格式错误对象的日志记录或其他响应
//作为练习:-)
为了{

字段非常感谢您的回答!是的,您是对的,使用选项对所有内容进行建模更有意义。但是,我不太确定如何动态创建匿名内部类。如果我需要为数据库中的每个对象创建一个这样的类(这些对象只不过是键值映射),我认为它不能很好地扩展。我添加了一个示例,说明如何使用将这些对象映射到数据库。