3

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

4

2 回答 2

2

In your case, when the pattern match goes to

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

The toAdd method will use the host, env you pass in addNewEntry(data, host, env). bob for host, flat for env. So, toAdd will always return <host name="bob" env="flat"/>.

Assume you have the client.xml like this:

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

The following code is how I try to get it done.

    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>

Besides, I notice one thing during the process. XML.load actually reverses attribute order, maybe it's irrelevant for solving your problem but just adding it here in case you need it.

于 2015-08-01T01:42:33.343 回答
0

https://github.com/geirolz/advxml

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

于 2019-08-03T13:29:08.033 回答