与多行 XML 案例的模式匹配

发布于 2024-08-17 14:26:38 字数 735 浏览 9 评论 0原文

我一定犯了一些愚蠢的错误。我有一个返回 XML 123 的服务器,现在我想与该 XML 进行匹配。所以我写了类似

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

这样的东西,只要我不需要处理多行 XML 文字,它就可以工作。因此,重要的是服务器将整个 XML 作为一行发送给我。 XML 足够大,可以分解一行代码,但我不知道如何让它工作。

服务器发送123123123123123< ;/f> 我想这样做:

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

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

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

I must be doing some stupid mistake. I have a server that returns the XML <a><b>123</b></a> and now I would like to match against that XML. So I write something like

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

This works as long as I do not have to deal with multi-line XML literals. So the important thing is that the server sends me the whole XML as a one-liner. The XML is large enough to explode a single line of code, but I can not figure out how to get this to work.

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 a MatchError. If I write everything in a single line it works. So the question is: how can I match XML while writing human-readable code?

I have of course tried to find an answer via Google. Funny enough all examples are one-liners or work recursive.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

若沐 2024-08-24 14:26:38

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

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

}

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

This is considerably uglier than I had initially imagined. I do have a partial solution, but I'm not sure it's worth the effort. The default pattern match treats whitespace as tokens, and I've not found any clean way to get around it. So I've done the opposite: decorate the input string with whitespace. This example has just a single level of indentation; you could imagine recursing the whitespace-addition to match your favorite indentation style.

Here's the example (need to compile and run; the 2.7 REPL at least doesn't seem to like multi-line 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 does (marginally) achieve what you want.

红玫瑰 2024-08-24 14:26:38

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

XML with and without newlines and other whitespace is not considered the same using "match". If you use scala.xml.Utility.trim, you can remove whitespace. (You probably want to trim both your input and what the server gives you unless you're positive the server will send you no whitespace.)

篱下浅笙歌 2024-08-24 14:26:38

也许你可以尝试这样的事情:

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

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

Perhaps you could try something like:

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

I'm not saying it will work... but it might

帅冕 2024-08-24 14:26:38

好吧,我没有解决匹配/大小写问题的方法。由于 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 图书

希望有帮助,

-- Flaviu Cipcigan

Well, I don't have a solution to the match/case problem. You do need an extractor which whitens the input xml due to how Scala pattern matching works -- you cannot apply trim to an xml literal which is a pattern, as that exists just at compile time, patterns being translated into a series of function calls at runtime.

However, to get the value of the c tag, you could always use the XPath like syntax of taking xml apart. For example, to get the value of c in your XML you could 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 from Programming in Scala (part of which is on Google books)

Hope it helps,

-- Flaviu Cipcigan

顾北清歌寒 2024-08-24 14:26:38

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

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>

因为 { {_} 求值中的空格是} 被忽略。

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

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

I came across a similar problem and found a clever solution:

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

I agree this should be a built-in feature in scala. When the pattern of the xml is complex, having to write it in a single line is really ugly.

It's easy to see why my solution works when you understand that matching against a pattern like:

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

is equivalent to matching against:

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

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

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

I'm new to scala and I noticed that this question is pretty old, so maybe a better way was found by now.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文