在 Scala 中编组/解组 XML

发布于 2024-10-11 20:49:02 字数 876 浏览 3 评论 0原文

我正在研究在 Scala 和 XML 之间编组/解组数据的各种方法,并且我有兴趣获得社区反馈(最好基于第一手知识/经验)。

我们目前正在使用 JAXB,这很好,但我希望有一个纯粹的 Scala 解决方案。我正在考虑以下方法:

  1. 使用 Scala 的内置 XML 工具:Scala->XML 会很容易,但我的猜测是另一个方向会相当痛苦。另一方面,这种方法支持任意翻译逻辑。

  2. 数据绑定scalaxb 目前看来有些不成熟,并没有处理我们当前的模式,我不知道 Scala 有任何其他数据绑定库。与 JAXB 一样,需要额外的转换层来支持涉及的转换。

  3. XML pickler 组合器GData Scala 客户端 库提供了 XML pickler 组合器,但最近的项目活动很少,我不知道当前状态如何。

问题

  1. 您对我列出的方法/库有何体验?
  2. 各自的相对优点和缺点是什么?
  3. 我还应该考虑其他方法或 Scala 库吗?

编辑:

我在自己对这个问题的回答中添加了一些关于我对pickler组合器的早期印象的注释,但我仍然对真正深入了解各种方法的人的反馈非常感兴趣。我希望得到一个比较全面的比较,以帮助开发人员选择适合他们需求的方法。

I am looking at various approaches for marshalling/unmarshalling data between Scala and XML, and I'm interested in getting community feedback (preferably grounded in first-hand knowledge/experience).

We're currently using JAXB, which is fine, but I'm hoping for a pure Scala solution. I'm considering the following approaches:

  1. Use Scala's built-in XML facilities: Scala->XML would be easy, but my guess is that the other direction would be fairly painful. On the other hand, this approach supports arbitrary translation logic.

  2. Data binding: scalaxb seems to be somewhat immature at the moment and doesn't handle our current schema, and I don't know of any other data binding library for Scala. Like JAXB, an extra translation layer is required to support involved transformations.

  3. XML pickler combinators: The GData Scala Client library provides XML pickler combinators, but recent project activity has been low and I don't know what the current status is.

Questions:

  1. What are your experiences with the approaches/libraries I've listed?
  2. What are the relative advantages and disadvantages of each?
  3. Are there any other approaches or Scala libraries that I should consider?

Edit:

I added some notes on my early impressions of pickler combinators in my own answer to this question, but I'm still very interested in feedback from someone who actually knows the various approaches in depth. What I'm hoping for is a somewhat comprehensive comparison that would help developers choose the right approach for their needs.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

尘曦 2024-10-18 20:49:02

我建议使用 Scala 的内置 XML 功能。我刚刚为如下所示的文档结构实现了反序列化:

val bodyXML = <body><segment uri="foo"><segment uri="bar" /></segment></body>

请注意,段可以相互嵌套。

段的实现如下:

case class Segment(uri: String, children: Seq[Segment])

要反序列化 XML,您可以执行以下操作:

val mySegments = topLevelSegments(bodyXML)

...topLevelSegments 的实现只需几行代码。请注意递归,它会挖掘 XML 结构:

def topLevelSegments(bodyXML: Node): Seq[Segment] = 
    (bodyXML \ "segment") map { nodeToSegment }

def nodeToSegment = (n: Node) => Segment((n \ "@uri")(0) text, childrenOf(n))

def childrenOf(n: Node): Seq[Segment] = (n \ "segment") map { nodeToSegment }

希望有帮助。

I recommend using Scala's built-in XML features. I've just implemented deserialization for a document structure that looks like this:

val bodyXML = <body><segment uri="foo"><segment uri="bar" /></segment></body>

Note that the segments can be nested within each other.

A segment is implemented as follows:

case class Segment(uri: String, children: Seq[Segment])

To deserialize the XML, you do this:

val mySegments = topLevelSegments(bodyXML)

...and the implementation of topLevelSegments is just a few lines of code. Note the recursion, which digs through the XML structure:

def topLevelSegments(bodyXML: Node): Seq[Segment] = 
    (bodyXML \ "segment") map { nodeToSegment }

def nodeToSegment = (n: Node) => Segment((n \ "@uri")(0) text, childrenOf(n))

def childrenOf(n: Node): Seq[Segment] = (n \ "segment") map { nodeToSegment }

Hope that helps.

半世蒼涼 2024-10-18 20:49:02

为了进行比较,我使用 < a href="http://code.google.com/p/gdata-scala-client/" rel="nofollow noreferrer">GData Scala Client 库:

def segment: Pickler[Segment] =
   wrap(elem("segment", 
           attr("uri", text) 
           ~ rep(segment))) {    // rep = zero or more repetitions
      // convert (uri ~ children) to Segment(uri, children), for unpickling
      Segment.apply 
   } {
      // convert Segment to (uri ~ children), for pickling
      (s: Segment) => new ~(s.uri, s.children toList)
   }

def body = elem("body", rep(segment))

case class Segment(uri: String, children: List[Segment])

此代码是指定两个方向所需的全部代码Segment 和 XML 之间的转换,而使用 Scala XML 库时,类似数量的代码仅指定一个转换方向。在我看来,这个版本也更容易理解(一旦你了解了 pickler DSL)。当然,正如 David 在评论中指出的那样,这种方法需要开发人员必须熟悉的额外依赖项和另一个 DSL。

将 XML 转换为段就像

body.unpickle(LinearStore.fromFile(filename)) // returns a PicklerResult[List[Segment]]

转换其他方式

xml.XML.save(filename, body.pickle(segments, PlainOutputStore.empty).rootNode)

一样简单,就组合器库而言,它似乎处于良好状态并在 Scala 2.8.1 中编译。我的初步印象是该库缺少一些可以很容易修复的细节(例如 oneOrMore 组合器)。我还没有时间看看它处理错误输入的效果如何,但到目前为止它看起来足以满足我的需求。

For comparison, I implemented David's example using the pickler combinators from the GData Scala Client library:

def segment: Pickler[Segment] =
   wrap(elem("segment", 
           attr("uri", text) 
           ~ rep(segment))) {    // rep = zero or more repetitions
      // convert (uri ~ children) to Segment(uri, children), for unpickling
      Segment.apply 
   } {
      // convert Segment to (uri ~ children), for pickling
      (s: Segment) => new ~(s.uri, s.children toList)
   }

def body = elem("body", rep(segment))

case class Segment(uri: String, children: List[Segment])

This code is all that is necessary to specify both directions of the translation between Segments and XML, whereas a similar amount of code specifies only one direction of the translation when using the Scala XML library. In my opinion, this version is also easier to understand (once you know the pickler DSL). Of course, as David pointed out in a comment, this approach requires an additional dependency and another DSL that developers have to be familiar with.

Translating XML to Segments is as simple as

body.unpickle(LinearStore.fromFile(filename)) // returns a PicklerResult[List[Segment]]

and translating the other way looks like

xml.XML.save(filename, body.pickle(segments, PlainOutputStore.empty).rootNode)

As far as the combinator library is concerned, it seems to be in decent shape and compiles in Scala 2.8.1. My initial impression is that the library is missing a few niceties (e.g. a oneOrMore combinator) that could be remedied fairly easily. I haven't had time to see how well it handles bad input, but so far it looks sufficient for my needs.

讽刺将军 2024-10-18 20:49:02

将 scala.xml.Node 写入字符串并不是什么大问题。 PrettyPrinter 应该小心您的需求。 scala.xml.XML.save()< /code>将写入文件和 scala.xml.XML.write() 输出到 Writer

Writing a scala.xml.Node to a string isn't a big deal. PrettyPrinter should take care of you needs. scala.xml.XML.save() will write to a file and scala.xml.XML.write() outputs to a Writer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文