С тех пор я узнал больше и представил то, что считаю лучшим решением в другом ответе. Я также исправил это, поскольку заметил, что не смог учесть ограничение subnode
.
Спасибо за вопрос! Я только что узнал некоторые интересные вещи, когда имел дело с XML. Вот что вы хотите:
def updateVersion(node: Node): Node = {
def updateNodes(ns: Seq[Node], mayChange: Boolean): Seq[Node] =
for(subnode <- ns) yield subnode match {
case <version>{ _ }</version> if mayChange => <version>2</version>
case Elem(prefix, "subnode", attribs, scope, children @ _*) =>
Elem(prefix, "subnode", attribs, scope, updateNodes(children, true) : _*)
case Elem(prefix, label, attribs, scope, children @ _*) =>
Elem(prefix, label, attribs, scope, updateNodes(children, mayChange) : _*)
case other => other // preserve text
}
updateNodes(node.theSeq, false)(0)
}
Теперь объяснение. Заявления первого и последнего случая должны быть очевидны. Последний существует, чтобы поймать те части XML, которые не являются элементами. Или, другими словами, текст. Обратите внимание, что в первом утверждении тест на флаг указывает, может ли version
быть изменен или нет.
Во втором и третьем операторах case будет использоваться сопоставитель шаблонов для объекта Elem. Это разделит элемент на все его составные части. Последний параметр, "children @ _ *", будет сопоставлять детей со списком чего угодно. Или, более конкретно, Seq [Node]. Затем мы восстанавливаем элемент с извлеченными частями, но передаем Seq [Node] updateNodes, выполняя шаг рекурсии. Если мы сопоставляем элемент subnode
, мы меняем флаг mayChange на true
, что позволяет изменить версию.
В последней строке мы используем node.theSeq для генерации Seq [Node] из Node и (0) для получения первого элемента Seq [Node], возвращенного в качестве результата. Поскольку updateNodes по сути является функцией карты (for ... yield переводится в карту), мы знаем, что в результате будет только один элемент. Мы передаем флаг false
, чтобы гарантировать, что version
не будет изменен, если элемент subnode
не является предком.
Есть немного другой способ сделать это, более мощный, но немного более многословный и неясный:
def updateVersion(node: Node): Node = {
def updateNodes(ns: Seq[Node], mayChange: Boolean): Seq[Node] =
for(subnode <- ns) yield subnode match {
case Elem(prefix, "version", attribs, scope, Text(_)) if mayChange =>
Elem(prefix, "version", attribs, scope, Text("2"))
case Elem(prefix, "subnode", attribs, scope, children @ _*) =>
Elem(prefix, "subnode", attribs, scope, updateNodes(children, true) : _*)
case Elem(prefix, label, attribs, scope, children @ _*) =>
Elem(prefix, label, attribs, scope, updateNodes(children, mayChange) : _*)
case other => other // preserve text
}
updateNodes(node.theSeq, false)(0)
}
Эта версия позволяет вам изменять любой тег "version", независимо от его префикса, атрибутов и области действия.