Вот еще один подход, который стоит рассмотреть. По сути, мы собираемся собирать scala.xml.Elem из строки и использовать некоторые запросы в стиле XPath.
import scala.xml._
def childUnion(parent: String, a: Elem, b: Elem): Elem = {
val open:String = "<" + parent + ">"
val close:String = "</" + parent + ">"
val children = a \\ parent \ "_" ++ b \\ parent \ "_"
return XML.loadString(open + children + close)
}
Сначала мы создали теги open
и close
, которые являются просто строками. Затем мы создаем children
, используя некоторый запрос в стиле XPath.
\\
- это оператор в Elem, который возвращает элементы и все подпоследовательности элемента.
\
аналогично, но возвращает элементы Элема.
"_"
- это подстановочный знак.
Почему не просто \
? У меня были проблемы с выяснением этого самостоятельно на основе документации, но взгляд на XPath для Java наводит меня на мысль, что \\
включает в себя весь Elem и дочерние элементы, тогда как \
включает только дочерние, поэтому если бы у нас было <parent><x/></parent> \ "parent"
, мы бы ничего не найти, так как только <x/>
пройдено.
Теперь этот метод не удивительный. Что мы можем сделать, чтобы сделать его немного более удивительным? Нам лучше использовать замечательный класс Option
в Scala и метод foldLeft
.
def childUnion(parent: String, a: Elem, b: Elem*): Option[Elem] = {
val parentElem = a \\ parent
parentElem.size match {
case 0 => None // no parent present
case _ =>
val children = b.foldLeft(parentElem \ "_")((d,c) => (d ++ (c \\ parent \ "_")))
val open:String = "<" + parent + ">"
val close:String = "</" + parent + ">"
Some(XML.loadString(open + children + close))
}
}
Это, конечно, имеет приятное дополнительное преимущество работы только с одним Elem, в случаях, когда родитель отсутствует, и переменное число Elem предоставляется в качестве аргументов. Вот длинный список примеров, которые я выполнил, придумывая этот последний метод,
scala> a
res85: scala.xml.Elem = <fruit> <apple></apple> <orange></orange> </fruit>
scala> b
res86: scala.xml.Elem = <fruit> <banana></banana> </fruit>
scala> c
res87: scala.xml.Elem = <box><fruit><apple></apple></fruit></box>
scala> d
res88: scala.xml.Elem = <box><nofruit></nofruit></box>
scala> e
res89: scala.xml.Elem = <fruit></fruit>
scala> val f = <fruit />
f: scala.xml.Elem = <fruit></fruit>
scala> childUnion("fruit", a)
res91: Option[scala.xml.Elem] = Some(<fruit><apple></apple><orange></orange></fruit>)
scala> childUnion("fruit", b)
res92: Option[scala.xml.Elem] = Some(<fruit><banana></banana></fruit>)
scala> childUnion("fruit", c)
res93: Option[scala.xml.Elem] = Some(<fruit><apple></apple></fruit>)
scala> childUnion("fruit", d)
res94: Option[scala.xml.Elem] = None
scala> childUnion("fruit", e)
res95: Option[scala.xml.Elem] = Some(<fruit></fruit>)
scala> childUnion("fruit", a, b)
res96: Option[scala.xml.Elem] = Some(<fruit><apple></apple><orange></orange><banana></banana></fruit>)
scala> childUnion("fruit", a, e)
res97: Option[scala.xml.Elem] = Some(<fruit><apple></apple><orange></orange></fruit>)
scala> childUnion("fruit", a, c)
res98: Option[scala.xml.Elem] = Some(<fruit><apple></apple><orange></orange><apple></apple></fruit>)
scala> childUnion("fruit", a, d)
res99: Option[scala.xml.Elem] = Some(<fruit><apple></apple><orange></orange></fruit>)
scala> childUnion("fruit", e, d)
res100: Option[scala.xml.Elem] = Some(<fruit></fruit>)
scala> childUnion("fruit", d, d)
res101: Option[scala.xml.Elem] = None
scala> childUnion("fruit", f)
res102: Option[scala.xml.Elem] = Some(<fruit></fruit>)