与嵌套JAXB一起使用的Scala和Java集合组合是什么?

时间:2011-09-24 18:17:02

标签: scala jaxb

使用JAXB,处理“嵌套”资源表示列表的标准方法(例如<products><product>X</product><product>Y</product></products>)是创建一个包装器对象,在Java中可能看起来像这样(借用Jhopify ):

@XmlType(name = "")
@XmlRootElement(name = "products")
public class ProductList {
  List<Product> products = new ArrayList<Product>();

  @XmlElement(name = "product", required = true)
  public List<Product> getProducts() { return products; }

  public void setProducts(List<Product> products) { this.products = products; }
}

但是,在将其转换为Scala时,我正在努力确定要使用哪些集合对象。在Mostly Blather blog上执行此操作有一个很好的介绍性帖子,它使用Scala Iterable与JCollection进行隐式转换(使用JavaConversions)。

这非常适合将JAXB类编组为XML,但遗憾的是,在解组时会在每次UnsupportedOperationException尝试时抛出add。基于此Scala documentation page的最后一段,看起来这是因为Java不区分其类型中的可变集合和不可变集合。

为了处理解组,我尝试了一种专门使用可变对象的替代方法:

@XmlType(name = "")
@XmlRootElement(name = "products")
class ProductList {
  private var products: Buffer[Product] = new ArrayBuffer[Product]

  @XmlElement(name = "product", required = true)
  def getProducts: JList[Product]  = products

  def setProducts(products: JList[Product]) {
    this.products = products
  }
}

但不幸的是,通过这种方法,解组给了我一个例外:

java.lang.NoSuchMethodError: ProductList.getProducts()Ljava/util/Collection;

编辑根据Travis的要求,这是我的解组代码:

val jaxbContext = JAXBContext.newInstance(ProductList.getClass())
val unmarshaller = jaxbContext.createUnmarshaller()
val root = unmarshaller.unmarshal(new StreamSource(new StringReader(responseString)), ProductList.getClass())
val r = root.getValue().asInstanceOf[ProductList]
val representations = r.getProducts.asScala.toList // Uses scalaj

所以我有点难过......我也看了scalaj's available conversions,但没有明显跳出来。任何帮助非常感谢!

1 个答案:

答案 0 :(得分:4)

你能发布你的解组代码吗?我已经用Scala中的JAXB做了类似的事情,你看起来像应该工作。这是一个完整的工作示例:

import javax.xml.bind.annotation._

class Thing {
  @scala.reflect.BeanProperty var name: String = _
}

@XmlRootElement(name = "things")
class Things {
  import scala.collection.JavaConversions._
  import scala.collection.mutable.Buffer

  private var things: Buffer[Thing] = Buffer[Thing]()

  @XmlElement(name = "thing", required = true)
  def getThings: java.util.List[Thing] = this.things

  def setThings(things: java.util.List[Thing]) {
    this.things = things
  }
}

我也会在Scala中编写测试代码,但它在Java中的工作方式相同。

object Things {
  import java.io.StringReader
  import java.io.StringWriter
  import javax.xml.bind.JAXBContext

  def main(args: Array[String]) {
    val thing1 = new Thing
    val thing2 = new Thing
    thing1.setName("Thing 1")
    thing2.setName("Thing 2")

    val list: java.util.List[Thing] = new java.util.ArrayList[Thing]
    list.add(thing1)
    list.add(thing2)

    val things = new Things
    things.setThings(list)

    val context = JAXBContext.newInstance(classOf[Things])
    val writer = new StringWriter
    context.createMarshaller.marshal(things, writer)

    println(writer.toString)

    val readThings = context.createUnmarshaller().unmarshal(
      new StringReader(writer.toString)
    ).asInstanceOf[Things]

    println("Size: " + readThings.getThings.size)
    println("Name of first: " + readThings.getThings.get(0).getName)
  }
}

这会编译并生成您期望的输出。