scala 中的递归 XML

发布于 2024-08-17 12:12:36 字数 990 浏览 3 评论 0原文

我正在尝试用 scala 解析此文档:

<?xml version="1.0"?>
<model>
    <joint name="pelvis">
            <joint name="lleg">
                    <joint name="lfoot"/>
            </joint>
            <joint name="rleg">
                    <joint name="rfoot"/>
            </joint>
    </joint>
</model>

我想用它为我的 2d 动画引擎创建一个骨架。每个关节都应该制作成相应的对象,并将所有子对象添加到其中。

所以这部分应该产生与此类似的结果:

j = new Joint("pelvis")
lleg = new Joint("lleg")
lfoot = new Joint("lfoot")
rleg = new Joint("rleg")
rfoot = new Joint("rfoot")
lleg.addJoint(lfoot)
rleg.addJoint(rfoot)
j.addJoint(lleg)
j.addJoint(rleg)

但是,我在浏览 xml 代码时遇到了问题。一方面,我不确定我是否完全理解语法 xml \\ "joint",它似乎会生成一个包含所有标签的 NodeSeq。


主要问题:

  1. 理解 scala 中 xml 语法的问题,即 xml \\ "...", Elem.child?,
  2. 从父节点获取属性而不从所有子节点获取属性时出现问题 ( xml \\ "@attribute",生成所有属性的串联..?)

I am trying to parse this document in scala:

<?xml version="1.0"?>
<model>
    <joint name="pelvis">
            <joint name="lleg">
                    <joint name="lfoot"/>
            </joint>
            <joint name="rleg">
                    <joint name="rfoot"/>
            </joint>
    </joint>
</model>

I want to use it to create a skeleton for my 2d-animation engine. Every joint should be made into the according object and all the children added to it.

So this part should produce a result similar to this:

j = new Joint("pelvis")
lleg = new Joint("lleg")
lfoot = new Joint("lfoot")
rleg = new Joint("rleg")
rfoot = new Joint("rfoot")
lleg.addJoint(lfoot)
rleg.addJoint(rfoot)
j.addJoint(lleg)
j.addJoint(rleg)

However, I am having trouble going through the xml code. For one thing, I am not sure I completely understand the syntax xml \\ "joint", which seems to produce a NodeSeq containing all tags.


Main problems:

  1. Problem understanding syntax with xml in scala, i.e. xml \\ "...", Elem.child?,
  2. Problem getting an attribute from a parent node without getting attributes from all children ( xml \\ "@attribute", produces a concat of all attributes..?)

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

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

发布评论

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

评论(3

人事已非 2024-08-24 12:12:36

运算符 \\ 是一个类似 XPath 的运算符。它将“选择”所有具有某种特征的后代。

这可以通过两遍来完成,如下所示:

val jointSeq = xml \\ "joint"
val jointMap = scala.collection.mutable.Map[String, Joint]

// First pass, create all joints
for {
  joint <- jointSeq
  names <- joint attribute "name"
  name <- names
} jointMap(name) = new Joint(name)

// Second pass, assign children
for {
  joint <- jointSeq
  names <- joint attribute "name"
  name <- names
  child <- joint \ "joint" // all direct descendants "joint" tags
  childNames <- child attribute "name"
  childName <- childNames
} jointMap(name).addJoint(jointMap(childName))

我想我更喜欢递归解决方案,但这应该是相当可行的。

The operator \\ is an XPath-like operator. It will "select" all descendants with a certain characteristic.

This could be done in two passes like this:

val jointSeq = xml \\ "joint"
val jointMap = scala.collection.mutable.Map[String, Joint]

// First pass, create all joints
for {
  joint <- jointSeq
  names <- joint attribute "name"
  name <- names
} jointMap(name) = new Joint(name)

// Second pass, assign children
for {
  joint <- jointSeq
  names <- joint attribute "name"
  name <- names
  child <- joint \ "joint" // all direct descendants "joint" tags
  childNames <- child attribute "name"
  childName <- childNames
} jointMap(name).addJoint(jointMap(childName))

I think I would prefer a recursive solution, but this should be quite workable.

☆獨立☆ 2024-08-24 12:12:36

使用 xtract 可以轻松完成此操作。

case class Joint(name: String, joints: Seq[Joint])
object Joint {
  implicit val reader: XmlReader[Joint] = (
    attribute[String]("name") and
    (__ \ "joint").lazyRead(seq(reader))
  )(apply _)
}

请注意如何使用lazyRead,以便可以递归地使用Joint 的读取器。

这篇博文更详细地讨论了 xtract:https://www.lucidchart.com/techblog/2016/07/12/introducing-xtract-a-new-xml-deserialization-library-for-scala/

免责声明:我在 Lucid Software 工作,并且是 xtract 的主要贡献者。

This can be done pretty easily using xtract.

case class Joint(name: String, joints: Seq[Joint])
object Joint {
  implicit val reader: XmlReader[Joint] = (
    attribute[String]("name") and
    (__ \ "joint").lazyRead(seq(reader))
  )(apply _)
}

Note how lazyRead is used so, that the reader for Joint can be used recursively.

This blog post, talks about xtract in more detail: https://www.lucidchart.com/techblog/2016/07/12/introducing-xtract-a-new-xml-deserialization-library-for-scala/

Disclaimer: I work for Lucid Software, and am a major contributor to xtract.

柳絮泡泡 2024-08-24 12:12:36

还有一个使用 scala.xml.pull.XMLEventReader 的解决方案:

val source = Source.fromPath("...") // or use fromString

var result: Joint = null

val xer = new XMLEventReader(source)
val ancestors = new Stack[Joint]()

while (xer.hasNext) {
  xer.next match {
    case EvElemStart(_, "joint", UnprefixedAttribute(_, name, _), _) =>
      val joint = new Joint(name.toString)
      if (ancestors.nonEmpty)
        ancestors.top.addJoint(joint)
      ancestors.push(joint)
    case EvElemEnd(_, "joint") =>
      result = ancestors.pop
    case _ =>
  }
}

println(result)

这是 Scala 2.8。

我已经发布了完整的源代码 此处。处理模型确实是顺序的,但效果很好,因为每个开放标记都将指示我们需要创建一个 Joint 对象,可以选择将其添加到父对象并存储为新的父对象。关闭标签会根据需要弹出父标签。

There is also a solution with the scala.xml.pull.XMLEventReader:

val source = Source.fromPath("...") // or use fromString

var result: Joint = null

val xer = new XMLEventReader(source)
val ancestors = new Stack[Joint]()

while (xer.hasNext) {
  xer.next match {
    case EvElemStart(_, "joint", UnprefixedAttribute(_, name, _), _) =>
      val joint = new Joint(name.toString)
      if (ancestors.nonEmpty)
        ancestors.top.addJoint(joint)
      ancestors.push(joint)
    case EvElemEnd(_, "joint") =>
      result = ancestors.pop
    case _ =>
  }
}

println(result)

This is Scala 2.8.

I have posted the complete source here. The processing model is really sequential, but that works well since every open tag will indicate that we need to create a Joint object, optionally add it to the parent and store as the new parent. Close tags pops parent as necessary.

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