Matching XML Multiline Flag Patterns

I must be making some kind of stupid mistake. I have a server that returns XML <a><b>123</b></a> , and now I would like to match this XML. So I am writing something like

 xml match { case <a><b>{_}</b></a> => true } 

This works until I need to deal with multi-line XML literals. Therefore, it is important that the server sends me all XML as single-line. The XML is large enough to blow a single line of code, but I cannot figure out how to do this.

The server sends <a><b>123</b><c>123</c><d>123</d><e>123</e><f>123</f></a> , and I would like to do this:

 xml match { case <a> <b>{_}</b> <c>{valueOfC}</c> <d>{_}</d> <e>{_}</e> <f>{_}</f> </a> => valueOfC } 

But I always get MatchError. If I write everything in one line, it will work. So the question is: how can I match XML when writing human-readable code?

Of course, I tried to find the answer through Google. Funnily enough, all examples are single-line or work recursively.

+6
xml scala pattern-matching
source share
5 answers

This is much uglier than I originally imagined. I have a partial solution, but I'm not sure if it is worth the effort. Matching the default pattern treats spaces as tokens, and I haven't found any clean way around it. So I did the opposite: decorate the input string with a space. This example has only one level of indentation; you could imagine how to rewrite the addition of spaces according to your favorite indentation style.

Here is an example (you need to compile and run, 2.7 REPL, at least not like multiline XML in case statements).

 object Test { import scala.xml._ def whiten(xml: Node,w:String): Node = { val bits = Node.unapplySeq(xml) val white = new Text(w) val ab = new scala.collection.mutable.ArrayBuffer[Node]() ab += white; bits.get._3.foreach {b => ab += b ; ab += white } new Elem( xml.prefix, xml.label, xml.attributes, xml.scope, ab: _* ); } val xml = <a><b>123</b><c>Works</c></a> def main(args:Array[String]) { whiten(xml,""" """ // You must match the multiline whitespace to your case indentation! ) match { case <a> <b>123</b> <c>{x}</c> </a> => println(x) case _ => println("Fails") } } } 

Rather inelegant, but it (slightly) achieves what you want.

+3
source share

XML with and without newlines and other spaces is not considered the same using match. If you use scala.xml.Utility.trim, you can remove the spaces. (You probably want to trim both your input and what the server gives you if you are not sure if the server will not send you spaces.)

+2
source share

Perhaps you could try something like:

 x match { case <a><b>{n @ _*}</b></a> => println(n) } 

I'm not saying that this will work ... but it may

+1
source share

Well, I don't have a solution to the match / case problem. You need an extractor that whitens the input xml due to how w60> works, you cannot apply trim to the xml literal, which is a template that exists only at compile time, and the templates are translated into a series of function calls at runtime.

However, to get the value of the c tag, you can always use XPath syntax to split xml. For example, to get the value of c in your XML, you can use:

 // a collection of all the values of all the c subelements (deep search) val c1 = (xml \\ "c").map(_.text.toInt) // same as above, but shallow val c2 = (xml \ "c").map(_.text.toInt) 

Also see the XML chapter on programming in Scala (some of which is on Google books )

Hope this helps,

- Flavy Chipchigan

0
source share

I ran into a similar problem and found a smart solution:

 xml match { case <a>{ <b>{_}</b>}{ <c>{valueOfC}</c>}{ <d>{_}</d>}{ <e>{_}</e>}{ <f>{_}</f> }</a> => valueOfC } 

I agree that this should be a built-in function in scala. When an xml template is complex, writing it on one line is very ugly.

It’s easy to understand why my solution works when you understand that pattern matching, for example:

 <a>{ <b>{_}</b> }</a> 

equivalent to matching with:

 <a><b>{_}</b></a> 

because the spaces in the evaluation { <b>{_}</b> } ignored.

Please note that you cannot use { <b>{_}</b><b>{_}</b> } . That's why my solution has }{ almost every line.

I am new to scala and I noticed that this question is pretty old, so maybe the best way was found.

0
source share

All Articles