【问题标题】:Add or append new element to XML file in Scala instead of replacing it在 Scala 中向 XML 文件添加或追加新元素,而不是替换它
【发布时间】:2015-07-31 19:01:06
【问题描述】:

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

【问题讨论】:

    标签: xml scala scala-2.10 scala-xml


    【解决方案1】:

    在你的情况下,当模式匹配到

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

    toAdd 方法将使用您传入的主机、env addNewEntry(data, host, env)。主机为 bob,环境为 flat。因此,toAdd 将始终返回 &lt;host name="bob" env="flat"/&gt;

    假设你有这样的 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,也许它与解决您的问题无关,但只需在此处添加以备不时之需。

    【讨论】:

    • @Zee,如果你看一下Elem的定义,你就会知道类型为Seq[Node]的configs实际上是指之间的Node,简单的测试方法是打印出来。
    • @Zee,嘿,你为什么要做这个 originalXML \\ "ClientConfig",我编辑了我的答案。看来您对 originalXML 的结构不是很清楚。我使用pattern来提取ClientConfig的Seq[Node],然后进行一些修改。
    • 对不起,是的,我只是在 XML 加载中加载整个文件。所以,我错过了什么,请告诉我。
    • 所以你的意思是在我要加载的文件中,结构实际上就像 ,你想添加 作为 的第一个孩子,对吗?
    【解决方案2】:

    https://github.com/geirolz/advxml

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-15
      • 1970-01-01
      • 2015-11-07
      • 2013-08-26
      • 2016-10-25
      • 2010-12-13
      • 1970-01-01
      • 2018-02-25
      相关资源
      最近更新 更多