Scala XML 构建:将子节点添加到现有节点 [英] Scala XML Building: Adding children to existing Nodes

查看:34
本文介绍了Scala XML 构建:将子节点添加到现有节点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 XML 节点,我想随着时间的推移向其中添加子项:

I Have an XML Node that I want to add children to over time:

val root: Node = <model></model>

但我看不到诸如 addChild() 之类的方法,因为我想写一些类似的内容:

But I cannot see methods such as addChild(), as I would like to write something along the lines of:

def addToModel() = {
    root.addChild(<subsection>content</subsection>)
}

因此,在一次调用此方法后,根 xml 将是:

So after a single call to this method the root xml would be:

<model><subsection>content</subsection></model>

我能看到的唯一能够附加节点的类是 NodeBuffer.我在这里遗漏了一些基本的东西吗?

The only class I can see that has the ability to append a Node is the NodeBuffer. Am I missing something fundamental here?

推荐答案

从这个开始:

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].

The method ++ works here because child is a Seq[Node], and newChild is a Node, which extends NodeSeq, which extends Seq[Node].

现在,这不会改变任何东西,因为 Scala 中的 XML 是不可变的.它将产生一个具有所需更改的新节点.唯一的成本是创建一个新的 Elem 对象,以及创建一个新的 Seq 子对象.子节点本身不会被复制,只是被引用,这不会导致问题,因为它们是不可变的.

Now, this doesn't change anything, because XML in Scala is immutable. It will produce a new node, with the required changes. The only cost is that of creating a new Elem object, as well as creating a new Seq of children. The children node, themselves, are not copied, just referred to, which doesn't cause problems because they are immutable.

但是,如果您将子项添加到 XML 层次结构中向下的节点,事情会变得复杂.一种方法是使用拉链,例如 这个博客.

However, if you are adding children to a node way down on the XML hierarchy, things get complicated. One way would be to use zippers, such as described in this blog.

但是,您可以使用 scala.xml.transform,其规则将更改特定节点以添加新子节点.首先,编写一个新的转换器类:

You can, however, use scala.xml.transform, with a rule that will change a specific node to add the new child. First, write a new transformer class:

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.

On Scala 2.7, replace head with first.

Scala 2.7 示例:

Example on 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>

如果只有父元素还不够,您可以更复杂地获取正确的元素.但是,如果您需要将子项添加到具有特定索引的通用名称的父项,那么您可能需要走拉链的方式.

You could make it more complex to get the right element, if just the parent isn't enough. However, if you need to add the child to a parent with a common name of a specific index, then you probably need to go the way of zippers.

例如,如果您有 <books><book/><book/></books>,并且您想添加 到第二个,这将很难用规则转换器来实现.你需要一个针对 books 的 RewriteRule,然后它会得到它的 child(它真的应该被命名为 children),找到 nth book 在其中添加新的子节点,然后重新组合子节点并构建新节点.可行,但如果您必须这样做太多,拉链可能会更容易.

For instance, if you have <books><book/><book/></books>, and you want to add <author/> to the second, that would be difficult to do with rule transformer. You'd need a RewriteRule against books, which would then get its child (which really should have been named children), find the nth book in them, add the new child to that, and then recompose the children and build the new node. Doable, but zippers might be easier if you have to do that too much.

这篇关于Scala XML 构建:将子节点添加到现有节点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆