2

Consider the following XML node:

 <Interval>
   <P v="1"/>
   <Q v="0.0"/>
  </Interval>

What is the correct way to pattern match the top level element in Scala? I would expect the following to work but it does not:

def visit(node:Node):String = {
    node match {
        case p @ <P/> => (p \ "@v") text
        case q @ <Q/> => (q \ "@v") text
        case <Interval> @ children:_* </Interval> => "parent"
    }
}
ashawley
  • 4,195
  • 1
  • 27
  • 40
kostas
  • 1,959
  • 1
  • 24
  • 43

2 Answers2

2

When you create an XML literal in Scala, the variable you assign to it is the top level element.

val node: scala.xml.Elem =
  <Interval>
   <P v="1"/>
   <Q v="0.0"/>
  </Interval> 

To match the top-level element, here Interval, or any element of Interval that has child elements like your example, you can use curly braces to match the children.

node match {
  case <Interval>{ children @_* }</Interval> => children.collect {
    case p @ <P/> => p \@ "v"
    case q @ <Q/> => q \@ "v"
  }
}

The result is:

Seq("1", "0.0")

If you're not familiar with collect, it allows you to provide a partial function (read as "pattern match") and ignore cases that would otherwise fail as a match error.

ashawley
  • 4,195
  • 1
  • 27
  • 40
  • 1
    I think that this answer mostly gave a more legible and intuitive in regard to original question than mine (on pattern matching syntax, *obviously*). It should be considered as the correct answer. (As for me, I'll use more often this syntax for simple use cases) – Benjamin Vialatou Apr 12 '19 at 12:43
0

This expression in scala:

<Interval>
 <P v="1"/>
 <Q v="0.0"/>
</Interval>

would definitely return a scala.xml.Node in scala but firstly a scala.xml.Elem. You can pattern match on it like this:

import scala.xml.Elem

def visit(elem: Elem) = {
    elem match {
        case e@Elem(_, "Interval",_, _, _*) => "identified!"
    }
}

Or you can pattern match on the child as well, because you pattern match on object n of type Elem is like Elem(n.prefix, n.label, n.attributes, n.scope, n.child) and here child elements (it's a Seq) are matched against each remaining elements of the pattern:

def visit(elem: Elem) = {
    elem match {
        case Elem(_, "Interval",_, _, emptyElem, nodeIWant@(<P/>), _*) =>
        (nodeIWant \ "@v").text
    }
}

which return 1 here for instance.

Hope it helps.

  • When I say "which return 1 here for instance" I may have to clarify that it is a `String` whom representation is `1`. Anyway, don't hesitate to ask for a more specific case if that's not sufficient here. – Benjamin Vialatou Apr 08 '19 at 14:35