代码之家  ›  专栏  ›  技术社区  ›  Joa Ebert Emran

与多行XML案例匹配的模式

  •  6
  • Joa Ebert Emran  · 技术社区  · 15 年前

    我一定是犯了个愚蠢的错误。我有一个返回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
    }
    

    但我总是得到一个匹配错误。如果我把每件事都写在一行里,那就行了。所以问题是:在编写人类可读的代码时,如何匹配XML?

    我当然想通过谷歌找到答案。有趣的是,所有的示例都是一行代码或递归工作。

    5 回复  |  直到 12 年前
        1
  •  3
  •   Rex Kerr    15 年前

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

    下面是示例(需要编译和运行;至少2.7repl不喜欢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")
      }
    }
    
    }
    

    相当不雅,但它确实(稍微)达到了你想要的效果。

        2
  •  2
  •   Rex Kerr    15 年前

    使用“match”时,带有或不带有换行符和其他空白的XML不被认为是相同的。如果使用scala.xml.utility.trim,则可以删除空白。(您可能希望同时调整输入和服务器提供的内容,除非您确定服务器不会向您发送空白。)

        3
  •  1
  •   Don Mackenzie    15 年前

    也许你可以尝试一下:

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

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

        4
  •  0
  •   Flaviu Cipcigan    15 年前

    嗯,我没有匹配/案例问题的解决方案。您确实需要一个提取器,它根据scala模式匹配的工作方式使输入XML变白——您不能应用 trim 到一个XML文本,它是一个模式,就像在编译时存在的那样,模式在运行时被转换成一系列函数调用。

    但是,要获得 c 标记,您可以始终使用类似xpath的语法来分离XML。例如,获取 C 在XML中,您可以使用:

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

    希望它有帮助,

    --弗拉维厄·西皮根

        5
  •  0
  •   Oren    12 年前

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

    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> } . 这就是为什么我的解决方案 }{ 几乎每一行。

    我是斯卡拉的新手,我注意到这个问题已经很老了,所以现在也许找到了一个更好的方法。