首页 > 代码库 > 快学Scala(16)--XML处理

快学Scala(16)--XML处理

读取,分析,创建和编写xml

1. XML字面量

val doc = <html><head><title>Fred‘s Memoir‘s</title></head><body>...</body></html>  //scala.xml.Elem
val items = <li>Fred</li><li>Wilma</li>  //scala.xml.NodeSeq

  注:有时,编译器会错误地识别出XML,例如x <y就会被理解为未结束的XML字面量

2. XML节点

Node类是所有XML节点类型的祖先。它的两个最重要的子类是Text和Elem。

  def main(args: Array[String]): Unit = {
    val elem = <a href = "http://scala-lang.org">The <em>Scala</em>language</a>
    for(n <- elem.child) {
      println(n)
    }
  }

  上述程序的输出结果为:

 技术分享

  可以看出elem节点序列

    val items = new NodeBuffer
    items += <li>Fred</li>
    items += <li>Wilma</li>
    val nodes: NodeSeq = items

有三个字节点,分别是两个Text节点和一个Elem节点。

3. 元素属性

要处理某个元素的属性键和值,可以用attributes属性。它将产出一个类型为MetaData的对象,该对象几乎就是但又不完全等同于一个从属性键到属性值的映射。你可以用()操作符来访问给定键的值。

val elem = <a href="http://scala-lang.org/">The Scala Language</a>
val url = elem.attributes("href")

  

val elem = <a href = "http://scala-lang.org">The <em>Scala</em> language</a>
val url = elem.attributes.get("href").getOrElse(Text(""))
for(attr <- elem.attributes) printf("attr.key: %s, attr.value.text: %s\n", attr.key, attr.value.text)

  输出结果为: attr.key: href, attr.value.text: http://scala-lang.org

val image = <img alt="TODO" src ="http://www.mamicode.com/hamster.jpg"/>
val map = image.attributes.asAttrMap
for(key <- map.keys) printf("key: %s, value: %s\n", key, map.get(key))

  

4. 内嵌表达式

你可以在XML字面量中包含Scala代码,动态的计算出元素内容

<ul><li>{items(0)}</li><li>{items(1)}</li></ul>
<ul>{for (i <- items) yield <li>{i}</li>}</ul>

  

5. 在属性中使用表达式

<img src=http://www.mamicode.com/{makeURL(filename)}/>

注:被引用的字符串当中的花括号不会被解析和求值,例如<img src=http://www.mamicode.com/“{makeURL(filename)}”/>将src属性设置为字符串“{makeURL(filename)}”。

如果内嵌代码块返回null或None,则该属性不会被设置

 

6. 特殊节点类型

    val js = <script><![CDATA[if (temp) < 0 alert("Cold")]]></script>

    val code = """if (temp < 0) alert ("Cold")"""
    val js1 = <script>{PCData(code)}</script>

  

    val g1 = <xml:group><li>Item 1</li><li>Item 2</li></xml:group>
    val g2 = Group(Seq(<li>Item 1</li>, <li>Item 2</li>))
    println(g2)

  输出结果为: <li>Item 1</li><li>Item 2</li>

 

7. 类XPath表达式

\操作符定位某个节点或节点序列的直接后代

    val list = <dl><dt>Java</dt><dd>Gosling</dd><dt>Scala</dt><dd>Odersky</dd></dl>
    val languages = list \ "dt"
    for(item <- languages) println(item)

  输出结果为: 

<dt>Java</dt>
<dt>Scala</dt>

通配符可以匹配任何元素

    val list = <ds><dl><dt>Java</dt><dd>Gosling</dd><dt>Scala</dt><dd>Odersky</dd></dl></ds>
    val languages = list \"_" \ "dt"
    for(item <- languages) println(item)

\\可以定位任何深度的后代

以@开头的字符串可以定位属性 img \ "@alt"将返回给定节点的alt属性,img \\ "@alt"将定位到img中任何元素的所有alt属性。但是不能使用通配符。

 

8. 模式匹配

node match {
  case <img/> => ...
  ...
}

  如果node是一个带有任何属性但没有后代的img元素,则第一个匹配会成功。

  处理单个后代的匹配表达式 case <li>{_}</li> => ...

  处理多个后代的匹配表达式 case <li>{_*}</li> => ...

除了使用通配符,也可以使用变量名。成功匹配到的内容会被绑定到该变量上

case <li>{child}</li> => child.text

要匹配一个文本节点,可以用如下这样的样例类匹配:  case <li>{Text(item)}</li> => item

要把节点序列绑到变量,使用如下语法:  case <li>{children @_*}</li> => for (c <- children) yield c

若要匹配到属性,得用守卫:  case n @ <img/> if (n.attributes("alt").text == "TODO") => ...

 

9. 修改元素和属性

val list = <ul><li>Fred</li><li>Wilma</li></ul>
val list2 = list.copy(label = "ol")
val list3 = list.copy(child = list.child ++ <li>Another item</li>)
println(list2)
println(list3)

输出结果为:   

<ol><li>Fred</li><li>Wilma</li></ol>
<ul><li>Fred</li><li>Wilma</li><li>Another item</li></ul>

要添加或删除属性,可以用%操作符

val image = <img src="http://www.mamicode.com/hamster.jpg"/>
val image2 = image % Attribute(null, "alt", "An image of a hamster", Attribute(null, "val", "frog.jpg", Null))
println(image2)

  输出结果为:<img val="frog.jpg" alt="An image of a hamster" src="http://www.mamicode.com/hamster.jpg"/>

10. XML变换

  def main(args: Array[String]): Unit = {
    val root = <ul><li>Fred</li><li>Wilma</li></ul>
    val rule1 = new RewriteRule {
      override def transform(n: Node): Seq[Node] = {
        case e @ <ul>{_*}</ul> => e.asInstanceOf[Elem].copy(label = "ol")
        case _ => n
      }
    }
    val transformed = new RuleTransformer(rule1).transform(root)
  }

  可以在RuleTransformer的构造器中给出多个规则:

val transformer = new RuleTransformer(rule1, rule2, rule3)

  

11. 加载和保存

import scala.xml.XML
val root = XML.loadFile("myfile.xml")
val root2 = XML.load(new FileInputStream("myfile.xml"))
val root3 = XML.load(new InputStreamReader(new FileInpurStream("myfile.xml"), "UTF-8"))
val root4 = XML.load(new URL("http://horstmann.com/index.html"))

  注:文档是使用Java类库中标准的SAX解析器加载的。但可惜并没有提供文档类型的定义。

要保存XML到文件中,可以用sava方法。

XML.save("myfile.xml", root)

  有三个可选参数:

  • enc用来指定字符编码(缺省为"ISO-8859-1")
  • xmlDecl用来指定输出中最开始是否要生成XML声明(<?xml...?>)(缺省为false)
  • doctype是样例类scala.xml.dtd.DocType的对象(缺省为null)

也可以保存到java.io.Writer,但是必须给出所有参数

XML.write(writer, root, "UTF-8", false, null)

  

12. 命名空间

xmlns属性可以声明一个命名空间

 

快学Scala(16)--XML处理