在Scala中向XML文件添加或附加新元素,而不是替换它

时间:2015-07-31 19:01:06

标签: xml scala scala-2.10 scala-xml

我的scala代码目前最终使用我添加的新标记替换了我的xml文件的整个部分。我希望它只将标记添加一次作为ClientConfig的子项,但它会将本节中的所有标记替换为自身。

val data = XML.load(file)
val p = new XMLPrettyPrinter(2)
val tryingtoAdd = addNewEntry(data,host,env)
p.write(tryingtoAdd)(System.out)

其中host = bob和env = flat先前已定义,addNewEntry定义如下

 private def isCorrectLocation(parent: Elem, node: Elem, host: String): Boolean = {
    parent.label == "ClientConfig" && node.label == "host"
  }

  def addNewEntry(elem:Elem, host: String, env: String): Elem ={
    val toAdd = <host name={host} env={env} />
    def addNew(current: Elem): Elem = current.copy(
      child = current.child.map {
        case e: Elem if isCorrectLocation(current, e, host) ⇒ toAdd
        case e: Elem ⇒ addNew(e)
        case other ⇒ other
      }
    )
    addNew(elem)
  }

它产生的xml是

<ClientConfig>
    <host name="bob" env="flat"/>
    <host name="bob" env="flat"/>
    <host name="bob" env="flat"/>
    <host name="bob" env="flat"/>
</ClientConfig>

而我希望它只是将它作为ClientConfig的单个子项附加,例如,最后三个子项已经存在于文件中

<ClientConfig>
    <host name="bob" env="flat"/>
    <host name="george" env="flat"/>
    <host name="alice" env="flat"/>
    <host name="bernice" env="flat"/>
</ClientConfig>

我该怎么办?例如,python有一个简单的插入方法

2 个答案:

答案 0 :(得分:2)

在您的情况下,模式匹配进入

case e: Elem if isCorrectLocation(current, e, host) => toAdd

toAdd方法将使用主机,env传入addNewEntry(data, host, env)。鲍勃为主机,平坦为环境。因此,toAdd将始终返回<host name="bob" env="flat"/>

假设您有这样的client.xml:

   <Root>
     <ServerConfig>
       <host name="allen" env="flat"/>
     </ServerConfig>
     <ClientConfig>
       <host name="george" env="flat"/>
       <host name="alice" env="flat"/>
       <host name="bernice" env="flat"/>
    </ClientConfig>
   </Root>

以下代码是我尝试完成它的方法。

    def toBeAddedEntry(name: String, env: String) = <host name={ name } env={ env } />
    def addNewEntry(originalXML: Elem, name: String, env: String) = {
      originalXML match {
         case e @ Elem(_, _, _, _, configs @ _*) => {
            val changedNodes = configs.map {
                case <ClientConfig>{ innerConfigs @ _* }</ClientConfig> => {
                    <ClientConfig> { toBeAddedEntry(name, env) ++ innerConfigs }</ClientConfig>
                }
                case other => other
             }
            e.copy(child = changedNodes)
         }
         case _ => originalXML
     }
   }   

    val originalXML = XML.load("client.xml")
    val printer = new scala.xml.PrettyPrinter(80,5)
    println(printer.format(addNewEntry(originalXML, "bob", "flat")))


    // result
    <Root>
      <ServerConfig>
        <host env="flat" name="allen"/>
     </ServerConfig>
     <ClientConfig>
       <host name="bob" env="flat"/>
       <host env="flat" name="george"/>
       <host env="flat" name="alice"/>
       <host env="flat" name="bernice"/>
    </ClientConfig>
   </Root>

此外,我在此过程中注意到一件事。 XML.load实际上reverses attribute order,也许它与解决你的问题无关,只是在你需要的时候添加它。

答案 1 :(得分:0)

https://github.com/geirolz/advxml

这是一个简单的库,可以简化XML转换