Scala: объединить XML-деревья данных? - PullRequest
5 голосов
/ 16 октября 2011

Мне любопытно, как лучше объединить набор деревьев xml, содержащих похожие данные в один набор (стиль 'union').

Я реализовал рабочее решение, но код выглядит плохо, и у меня есть сильная интуиция, что должен быть более приятный и компактный способ реализуя это.

Я пытаюсь в простейшем случае объединить что-то вроде:

<fruit> <apple /> <orange /> </fruit>

и

<fruit> <banana /> </fruit>

Кому:

<fruit> <apple/> <orange/> <banana/> </fruit>

Какие-нибудь хорошие идеи, как сделать чистую реализацию этого в Scala?

Ответы [ 2 ]

1 голос
/ 11 августа 2012

Вот еще один подход, который стоит рассмотреть. По сути, мы собираемся собирать 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>)
1 голос
/ 16 октября 2011

с

val appleAndOrange : Elem = <fruit> <apple/> <orange/> </fruit>

и

val banana : Elem = <fruit> <banana> </fruit>

вы можете сделать

val all = appleAndOrange.copy(child = appleAndOrange.child ++ banana.child)

Однако это просто берет метку <fruit> из appleAndOrangeи проигнорируйте тот из banana, который здесь тоже самое.То же самое, вы должны решить, какие проверки вы хотите и какое поведение, если они не совпадают.То же самое для префиксов, атрибутов и областей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...