如何创建类型化工厂方法构造函数的类层次结构,并使用抽象类型从Scala访问它们?

时间:2011-11-17 05:00:45

标签: scala

(基本上我需要对这两个问题进行某种综合(12),但我不够聪明,不能将它们自己组合起来。)

我在Scala中有一组JAXB表示,如下所示:

abstract class Representation {
  def marshalToXml(): String = {
    val context = JAXBContext.newInstance(this.getClass())
    val writer = new StringWriter
    context.createMarshaller.marshal(this, writer)
    writer.toString()
  }
}

class Order extends Representation {
  @BeanProperty
  var name: String = _
  ...
}
class Invoice extends Representation { ... }

我遇到的问题是我的解组“构造函数”方法:

def unmarshalFromJson(marshalledData: String): {{My Representation Subclass}} = {
  val mapper = new ObjectMapper()
  mapper.getDeserializationConfig().withAnnotationIntrospector(new JaxbAnnotationIntrospector())
  mapper.readValue(marshalledData, this.getClass())
}

def unmarshalFromXml(marshalledData: String): {{My Representation Subclass}} = {
  val context = JAXBContext.newInstance(this.getClass())
  val representation = context.createUnmarshaller().unmarshal(
    new StringReader(marshalledData)
  ).asInstanceOf[{{Type of My Representation Subclass}}]
  representation // Return the representation
}

具体来说,我无法弄清楚如何以类型安全和干燥的方式将这些解组方法附加到我的每个类,然后从Scala调用它们(希望有时只使用抽象类型信息)。换句话说,我想这样做:

val newOrder = Order.unmarshalFromJson(someJson)

更加雄心勃勃:

class Resource[R <: Representation] {
    getRepresentation(marshalledData: String): R = 
        {{R's Singleton}}.unmarshalFromXml(marshalledData)
}

就我特别绊脚石而言:

  • 我无法弄清楚是否应该在unmarshalFrom*()类或单个Representation对象中定义一次Representation构造函数 - 如果是后者,我看不到如何通过OrderInvoice等的类层次结构自动继承它。
  • 我无法让this.typeas per this answer)作为一种自我键入的方式unmarshalFromJson() - 我在{{1}上收到编译错误type mismatch; found: ?0 where type ?0 required: Representation.this.type调用
  • 我无法弄清楚如何使用隐式readValue()模式(as per this answer)来解决我的Representation类层次结构,仅使用类型信息调用单例解组构造函数

我知道这是一个涉及各种不同(但相关)问题的巨大问题 - 感激不尽的任何帮助!

亚历

2 个答案:

答案 0 :(得分:2)

关键是不要尝试将方法附加到类,而是将其作为参数传递。要指出你期望的类型,并让类型系统处理它传入。我试图使unmarshal调用看起来像DSL一样。

val order = UnMarshalXml( xml ).toRepresentation[Order]

以下是完全可测试的代码段

abstract class Representation {
  def marshalToXml(): String = {
      val context = JAXBContext.newInstance(this.getClass)
      val writer = new StringWriter
      context.createMarshaller.marshal(this, writer)
      writer.toString
  }
}

@XmlRootElement
class Order extends Representation {
  @BeanProperty
  var name: String = _
}

case class UnMarshalXml( xml: String ) {
  def toRepresentation[T <: Representation](implicit m:Manifest[T]): T = {
    JAXBContext.newInstance(m.erasure).createUnmarshaller().unmarshal(
      new StringReader(xml)
    ).asInstanceOf[T]
  }
}

object test {
  def main( args: Array[String] ) {
    val order = new Order
    order.name = "my order"
    val xml = order.marshalToXml()
    println("marshalled: " + xml )
    val received = UnMarshalXml( xml ).toRepresentation[Order]
    println("received order named: " + received.getName )
  }
}

如果运行test.main

,应该会看到以下输出
marshalled: <?xml version="1.0" encoding="UTF-8" standalone="yes"?><order><name>my order</name></order>
received name: my order

答案 1 :(得分:0)

以下是Neil代码的更新版本,我用它来支持第二个用例以及第一个用例:

case class UnmarshalXml(xml: String) {

  def toRepresentation[T <: Representation](implicit m: Manifest[T]): T =
    toRepresentation[T](m.erasure.asInstanceOf[Class[T]])

  def toRepresentation[T <: Representation](typeT: Class[T]): T =
    JAXBContext.newInstance(typeT).createUnmarshaller().unmarshal(
      new StringReader(xml)
    ).asInstanceOf[T]
}

这支持如下的简单示例:

val order = UnmarshalXml(xml).toRepresentation[Order]

但是对于基于抽象类型的用法,您可以这样使用:

val order = UnmarshalXml(xml).toRepresentation[T](typeOfT)

(在声明typeOfT时使用其他implicit Manifest抓取并存储T的位置。)