Domanda

Sto cercando di analizzare questo documento 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>

voglio usarlo per creare uno scheletro per il mio motore 2d-animazione. Ogni comune dovrebbe essere fatto in oggetto secondo e tutti i bambini aggiunto ad esso.

Quindi questa parte dovrebbe produrre un risultato simile a questo:

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)

Tuttavia, sto avendo difficoltà passare attraverso il codice XML. Per prima cosa, io non sono sicuro di aver capito completamente il xml \\ "joint" sintassi, che sembra produrre un NodeSeq che contiene tutti i tag.


I problemi principali:

  1. Problemi comprendere sintassi XML in scala, cioè xml \\ "...", Elem.child?,
  2. Problema ottenere un attributo da un nodo padre senza ottenere gli attributi da tutti i bambini (xml \\ "@attribute", produce un concat di tutti gli attributi ..?)
È stato utile?

Soluzione

Il \\ operatore è un operatore XPath-like. Sarà "selezionare" tutti i discendenti di una determinata caratteristica.

Questo potrebbe essere fatto in due passate in questo modo:

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

Credo che io preferirei una soluzione ricorsiva, ma questo dovrebbe essere abbastanza praticabile.

Altri suggerimenti

Questo può essere fatto abbastanza facilmente utilizzando 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 _)
}

Si noti come lazyRead viene utilizzato in modo, che il lettore per Joint può essere utilizzato in modo ricorsivo.

Questo post del blog, parla di Xtract in modo più dettagliato: https://www.lucidchart.com/techblog/2016/07/12/introducing-xtract-a-new-xml-deserialization-library-for-scala/

Disclaimer: io lavoro per Lucid Software, e sono un importante contributo per Xtract

.

C'è anche una soluzione con il 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)

Questo è Scala 2.8.

Ho inviato il sorgente completo qui . Il modello di elaborazione è davvero sequenziale, ma che funziona bene dal momento che ogni tag di apertura indica che abbiamo bisogno di creare un oggetto comune, facoltativamente aggiungerlo al genitore e negozio come il nuovo genitore. Chiudi tag pops genitore, se necessario.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top