【问题标题】:Pattern matching with multiline XML case与多行 XML 案例匹配的模式
【发布时间】:2010-01-12 20:02:01
【问题描述】:

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

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

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

服务器发送&lt;a&gt;&lt;b&gt;123&lt;/b&gt;&lt;c&gt;123&lt;/c&gt;&lt;d&gt;123&lt;/d&gt;&lt;e&gt;123&lt;/e&gt;&lt;f&gt;123&lt;/f&gt;&lt;/a&gt;,我想这样做:

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

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

我当然尝试通过 Google 找到答案。有趣的是,所有示例都是单行或递归工作。

【问题讨论】:

  • 您希望如何指定元素之间的空白很重要的 case 语句?
  • 您必须同时支持恕我直言。 E4X 使用 XML.ignoreWhitespace 的方法范围配置来定义它。因此,每当您想显式忽略空白时,您都可以说“XML.ignoreWhitespace = true”。

标签: xml scala pattern-matching


【解决方案1】:

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

这是示例(需要编译和运行;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")
  }
}

}

相当不雅,但它确实(勉强)实现了你想要的。

【讨论】:

  • 感谢 Rex 的努力。但是我想知道为什么这个相当明显的案例会造成如此大的压力。
  • 我相信它有效,但 TBH 治疗看起来比问题更糟糕,我只会把所有东西放在一条线上。
【解决方案2】:

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

【讨论】:

  • 错误:方法 trim 不是案例类构造函数,也没有 unapply/unapplySeq 方法
【解决方案3】:

也许您可以尝试以下方法:

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

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

【讨论】:

  • 好吧,但是你如何匹配这样的 a-b-c-d-e-f 案例呢?这可能会变得非常复杂,并且您会丢失有关比赛顺序的信息。
  • 我刚刚在 REPL val x = 123123123123 x 匹配 { case {m @ _*} => println(m.getClass.getName) } 和我得到“scala.xml.NodeBuffer”,内容为 ArrayBuffer(123, 123, , 123, 123)
  • 是的,但是你将如何匹配它满足条件并提取值?如果我没记错的话,您将不得不在一场比赛之后进行连锁比赛,对吧?
  • 我明白你的意思,抱歉 Joa,我看到了你关于多部分匹配的问题的第一部分,并使用了我在 Jesse Eichar 的博客中看到的内容。关于你问题的第二部分,我会继续努力。
  • 在我看来,“最好的”解决方案是将所有丑陋的匹配代码隐藏在提取器后面,因为它是唯一可重用的方法。否则我必须在 n 个地方编辑比赛疯狂。但话又说回来,这样的东西不是真正可读的“xml match { case Entity(_, _, _, _, _, Some(value), _, _, _) => {} }”。
【解决方案4】:

好吧,我没有解决匹配/案例问题的方法。由于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)

另请参阅 Programming in Scala 中的 XML 章节(部分位于 Google books

希望对你有帮助,

-- 弗拉维乌·西普西根

【讨论】:

    【解决方案5】:

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

    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>
    

    因为{ &lt;b&gt;{_}&lt;/b&gt; } 计算中的空格被忽略了。

    但请注意,您不能使用{ &lt;b&gt;{_}&lt;/b&gt;&lt;b&gt;{_}&lt;/b&gt; }。这就是为什么我的解决方案几乎每一行都有一个}{

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-08-12
      • 1970-01-01
      • 2023-03-26
      • 2013-09-13
      • 2017-02-25
      • 1970-01-01
      • 2020-09-19
      相关资源
      最近更新 更多