Хорошо, начните с этого:
def addChild(n: Node, newChild: Node) = n match {
case Elem(prefix, label, attribs, scope, child @ _*) =>
Elem(prefix, label, attribs, scope, child ++ newChild : _*)
case _ => error("Can only add children to elements!")
}
Метод ++
работает здесь, потому что child
это Seq[Node]
, а newChild
это Node
, что расширяет NodeSeq
,который расширяет Seq[Node]
.
Теперь это ничего не меняет, потому что XML в Scala неизменен.Он создаст новый узел с необходимыми изменениями.Единственная стоимость - это создание нового Elem
объекта, а также создание Seq
дочерних объектов.Дочерние узлы сами по себе не копируются, а просто упоминаются, что не вызывает проблем, поскольку они неизменны.
Однако, если вы добавляете дочерние узлы в узел вниз по иерархии XML, все становитсясложно.Один из способов - использовать застежки-молнии, как описано в этом блоге .
Однако вы можете использовать scala.xml.transform
с правилом, которое изменит конкретный узел для добавления новогоребенок.Сначала напишите новый класс преобразователя:
class AddChildrenTo(label: String, newChild: Node) extends RewriteRule {
override def transform(n: Node) = n match {
case n @ Elem(_, `label`, _, _, _*) => addChild(n, newChild)
case other => other
}
}
Затем используйте его следующим образом:
val newXML = new RuleTransformer(new AddChildrenTo(parentName, newChild)).transform(oldXML).head
В Scala 2.7 замените head
на first
.
Пример на Scala 2.7:
scala> val oldXML = <root><parent/></root>
oldXML: scala.xml.Elem = <root><parent></parent></root>
scala> val parentName = "parent"
parentName: java.lang.String = parent
scala> val newChild = <child/>
newChild: scala.xml.Elem = <child></child>
scala> val newXML = new RuleTransformer(new AddChildrenTo(parentName, newChild)).transform(oldXML).first
newXML: scala.xml.Node = <root><parent><child></child></parent></root>
Вы можете усложнить получение нужного элемента, если недостаточно только родительского элемента.Однако, если вам нужно добавить дочерний элемент к родителю с общим именем определенного индекса, вам, вероятно, нужно пойти по пути молний.
Например, если у вас есть <books><book/><book/></books>
, и вы хотите добавить <author/>
ко второму, это будет трудно сделать с помощью преобразователя правил.Вам понадобится RewriteRule против books
, который затем получит его child
(который действительно должен был бы называться children
), найдите в них n th book
, добавьте новыйдочерний к этому, а затем перекомпоновать дочерние элементы и построить новый узел.Выполнимо, но молнии могут быть проще, если вам придется делать это слишком много.