与多行XML案例匹配的模式

时间:2010-01-12 20:02:01

标签: xml scala pattern-matching

我一定是犯了一些愚蠢的错误。我有一个返回XML <a><b>123</b></a>的服务器,现在我想与该XML匹配。所以我写了像

这样的东西
xml match {
  case <a><b>{_}</b></a> => true
}

只要我不必处理多行XML文字,这就可以工作。所以重要的是服务器将整个XML作为单行发送给我。 XML足够大,可以爆炸一行代码,但我无法弄清楚如何让它工作。

服务器发送<a><b>123</b><c>123</c><d>123</d><e>123</e><f>123</f></a>,我想这样做:

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

但我总是得到一个MatchError。如果我在一行中写下所有内容就行了。所以问题是:如何在编写人类可读的代码时匹配XML?

我当然试图通过谷歌找到答案。有趣的是,所有的例子都是单行或递归的。

5 个答案:

答案 0 :(得分:3)

这比我最初设想的要难得多。我确实有部分解决方案,但我不确定这是值得的。默认模式匹配将空格视为标记,我没有找到任何干净的方法来绕过它。所以我做了相反的事情:用空格装饰输入字符串。这个例子只有一个级别的缩进;您可以想象再次添加空格以匹配您最喜欢的缩进样式。

这是示例(需要编译和运行; 2.7 REPL至少似乎不喜欢case语句中的多行XML)。

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

}

相当不优雅,但确实(略微)实现了你想要的东西。

答案 1 :(得分:2)

使用“匹配”时,使用和不使用换行符和其他空格的XML不会被视为相同。如果使用scala.xml.Utility.trim,则可以删除空格。 (您可能希望修改输入和服务器提供的内容,除非您肯定服务器不会向您发送任何空格。)

答案 2 :(得分:1)

也许你可以尝试类似的东西:

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

我不是说它会起作用......但它可能

答案 3 :(得分:0)

好吧,我没有匹配/案例问题的解决方案。由于Scala模式匹配的工作原理,您需要一个提取输入xml的提取器 - 您不能将trim应用于作为模式的xml文字,因为它只是在编译时存在,模式被转换为一个系列在运行时调用函数。

但是,要获取c标记的值,您可以始终使用类似XPath的语法将xml分开。例如,要在XML中获取c的值,您可以使用:

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

另请参阅Scala编程中的XML章节(其中一部分位于Google books

希望它有所帮助,

- Flaviu Cipcigan

答案 4 :(得分:0)

我遇到了类似的问题并找到了一个聪明的解决方案:

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

我同意这应该是scala中的内置功能。当xml的模式很复杂时,必须将它写在一行中真的很难看。

当您理解与以下模式匹配时,我很容易理解为什么我的解决方案有效:

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

相当于匹配:

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

因为{ <b>{_}</b> }评估中的空格被忽略。

但请注意,您无法使用{ <b>{_}</b><b>{_}</b> }。这就是为什么我的解决方案几乎每行都有一个}{

我是scala的新手,我注意到这个问题很老了,所以现在可能找到了更好的方法。