Сопоставление с шаблоном с многострочным регистром XML - PullRequest
6 голосов
/ 12 января 2010

Я, должно быть, делаю какую-то глупую ошибку. У меня есть сервер, который возвращает 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 при написании читабельного кода?

Я, конечно, пытался найти ответ через Google. Достаточно забавно, что все примеры являются однострочными или рекурсивными.

Ответы [ 5 ]

3 голосов
/ 13 января 2010

Это гораздо страшнее, чем я предполагал изначально. У меня есть частичное решение, но я не уверен, что оно того стоит. Сопоставление с шаблоном по умолчанию рассматривает пробелы как токены, и я не нашел никакого чистого способа обойти это. Так что я сделал наоборот: украсить входную строку пробелом. Этот пример имеет только один уровень отступа; Вы можете себе представить повторение дополнения пробелами в соответствии с вашим любимым стилем отступа.

Вот пример (нужно скомпилировать и запустить; по крайней мере, 2,7 REPL не похоже на многострочный XML в операторах case).

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 голосов
/ 12 января 2010

XML с символами новой строки и без них, а также с другими пробелами не считается одинаковым при использовании «match». Если вы используете scala.xml.Utility.trim, вы можете удалить пробелы. (Возможно, вы захотите урезать как ваш ввод, так и то, что сервер дает вам, если вы не уверены, что сервер не отправит вам пробелы.)

1 голос
/ 13 января 2010

Возможно, вы могли бы попробовать что-то вроде:

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

Я не говорю, что это будет работать ... но это может

0 голосов
/ 02 марта 2013

Я столкнулся с подобной проблемой и нашел умное решение:

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, и я заметил, что этот вопрос довольно старый, так что, может быть, лучший способ уже найден.

0 голосов
/ 13 января 2010

Ну, у меня нет решения проблемы соответствия / случая. Вам нужен экстрактор, который отбеливает входной XML-файл из-за того, как работает сопоставление с шаблоном Scala - вы не можете применить 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)

Также см. Главу XML из раздела Программирование в Scala (часть из которой находится на Google books )

Надеюсь, это поможет,

- Флавиу Чипчиган

...