Как создать иерархию классов типизированных конструкторов фабричных методов и получить к ним доступ из Scala, используя абстрактные типы? - PullRequest
1 голос
/ 17 ноября 2011

(По сути, мне нужен какой-то синтез этих двух вопросов ( 1 , 2 ), но я не достаточно умен, чтобы объединить их сам.)

У меня есть набор представлений JAXB в Scala, например:

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 объекте - если последний, я не вижу, как я могу автоматически наследовать это черезиерархия классов Order, Invoice и т. д.
  • Я не могу заставить this.type ( согласно этому ответу ) работать как способ самонабора unmarshalFromJson()- Я получаю ошибку компиляции type mismatch; found: ?0 where type ?0 required: Representation.this.type на readValue() вызове
  • Я не могу понять, как использовать неявный шаблон Default[A] ( согласно этому ответу ) для работывниз по моей иерархии классов представления для вызова одноэлементных демаршаллинговых конструкторов, использующих только информацию о типе

Я знаю, что это немного гигантский вопрос, касающийся различных (но связанных) вопросов - любая помощь будет с благодарностью получена!

Алекс

Ответы [ 2 ]

2 голосов
/ 17 ноября 2011

Ключ заключается в том, чтобы не пытаться присоединить метод к классу, а передать его в качестве параметра.Чтобы указать тип, который вы ожидаете, и позволить системе типов обрабатывать его передачу. Я попытался сделать немаршальный вызов чем-то вроде небольшого 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
, вы должны увидеть следующий вывод
0 голосов
/ 17 ноября 2011

Вот обновленная версия кода Нила, который я использовал для поддержки второго варианта использования, а также первого:

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.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...