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

Add or append new element to XML file in Scala instead of replacing it

我的 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 有一个简单的插入方法

在你的例子中,当模式匹配到

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

toAdd 方法将使用您传入的主机、环境 addNewEntry(data, host, env)。 bob 代表主机,flat 代表环境。因此,toAdd 将始终 return <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,也许它与解决您的问题无关,但只需在此处添加它以备不时之需。

https://github.com/geirolz/advxml

这是一个简单的库,用于简化 XML 转换