Scala:如何将新节点附加到XML文件以及如何从XML文件中删除节点

时间:2015-08-20 14:19:07

标签: xml scala

我刚刚开始使用Scala进行编码,我在使用这个非常基本的XML结构时遇到了困难:

<Countries>

    <Country>
        <Name>Italy</Name>
        <Continent>Europe</Continent>
    </Country>

    <Country>
        <Name>Japan</Name>
        <Continent>Asia</Continent>
    </Country>

</Countries>

问题1:我想在文件中添加一个新节点(一个新的国家/地区)。我使用XML.loadFile成功加载了文件,但不知道如何添加新节点,然后保存文件。

问题2:我还想删除文件中的节点,但即使在这里,我也很难找到我想要的东西,特别是因为我想删除元素与用户输入的国家名称匹配的节点。

我使用了我在网上找到的一段代码:

val removeIt = new RewriteRule {
    override def transform(n: Node): NodeSeq = n match {
        case e: Elem if (e \ "Name").text == "Japan" => NodeSeq.Empty
        case n => n
    }
}

这样可行,但不幸的是它返回一个不被接受作为Xml.Save参数的NodeSeq,而且我也不知道如何传递一个String参数来确定要删除的节点。

1 个答案:

答案 0 :(得分:3)

假设我将您的示例x​​ml代码放在Country.xml中。

<Countries>
 <Country>
    <Name>Italy</Name>
    <Continent>Europe</Continent>
 </Country>

 <Country>
     <Name>Japan</Name>
     <Continent>Asia</Continent>
 </Country>
</Countries>

以下是我如何尝试完成它

object XMLLoader extends App {

    def toBeAddedEntry(name: String, continent: String) =
        <Country>
            <Name>{ name }</Name>
            <Continent>{ continent }</Continent>
        </Country>

    // For problem 1 How to add a new Node
    def addNewEntry(originalXML: Elem, name: String, continent: String) = {
        originalXML match {
            case <Countries>{ innerProps @ _* }</Countries> => {
                <Countries> {  
                    innerProps ++ toBeAddedEntry(name, continent) 
                }</Countries>
            }
            case other => other
        }
    }

    // For problem 2 How to delete node with element Name with certain value
    def deleteEntry(originalXML: Elem, nameValue: String) = {
        originalXML match {
            /*
                 Considering you just start coding in Scala, the following explanation may help:
                 Here Elem is used as Extractor, actually the unapplySeq in Elem object is invoked
                 def unapplySeq(n: Node) = n match {
                     case _: SpecialNode | _: Group  => None
                     case _ => Some((n.prefix, n.label, n.attributes, n.scope, n.child))
                 }
                 Then we use sequence pattern(match against a sequence without specifying how long it can be) to
                 extract child of originalXML and do the filtering job
            */

            case e @ Elem(_, _, _, _, countries @ _*) => {
                /*
                    original is kind of like
                        <Country>
                            <Name>Japan</Name>
                            <Continent>Asia</Continent>
                        </Country>
                */
                val changedNodes = countries filter { country =>
                    (original \ "Name").exists(elem => elem.text != nameValue)
                }
                e.copy(child = changedNodes)
            }
            case _ => originalXML
        }
    }

     // define your own way to load Country.xml
    val originalXML = XML.load(getClass.getClassLoader.getResourceAsStream("Country.xml"))
    val printer = new scala.xml.PrettyPrinter(80,5)
    println(printer.format(addNewEntry(originalXML, "China", "Asia")))
    println(printer.format(deleteEntry(originalXML, "Japan")))
}

结果如下:

<Countries>
 <Country>
      <Name>Italy</Name>
      <Continent>Europe</Continent>
 </Country>
 <Country>
      <Name>Japan</Name>
      <Continent>Asia</Continent>
 </Country>
 <Country>
      <Name>China</Name>
      <Continent>Asia</Continent>
 </Country>
</Countries>


<Countries>
 <Country>
      <Name>Italy</Name>
      <Continent>Europe</Continent>
 </Country>
</Countries>

剩下的部分是关于将Node写回Country.xml。无论如何,希望它有所帮助。