views:

143

answers:

2

Hello Everyone,

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.

I hope you guys have some pointers for me.


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

Thanks in advande :)

+4  A: 

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.

Daniel
A: 

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.

huynhjl