Scala入門到精通——第二十七節 Scala操縱XML
本節主要內容
- XML 字面量
- XML內容提取
- XML物件序列化及反序列化
- XML檔案讀取與儲存
- XML模式匹配
1. XML 字面量
XML是一種非常重要的半結構化資料表示方式,目前大量的應用依賴於XML,這些應用或利用XML作為資料交換格式,或利用XML進行檔案配置等。像JAVA、C++及其它流行的程式開發語言都是依賴於第三方庫來實現XML的操作,例如JAVA經常通過JDOM,DOM4J等XML處理工具進行XML的操縱,但Scala提供了對XML的原生支援,通過scala.xml._包下的類或物件可以進行任何的XML操作。下面的程式碼演示了Scala中如何定義XML字面量。
scala> var x: scala.xml.Elem = <site ><name>xuetuwuyou</name><url>http://www.xuet
uwuyou.com/</url></site>
x: scala.xml.Elem = <site><name>xuetuwuyou</name><url>http://www.xuetuwuyou.com/
</url></site>
scala> <site><name>xuetuwuyou</name><url>http://www.xuetuwuyou.com/</url ></site>
res8: scala.xml.Elem = <site><name>xuetuwuyou</name><url>http://www.xuetuwuyou.c
om/</url></site>
通過上面的程式碼不能發現,scala會自動地對XML進行解析,並識別為scala.xml.Elem型別。scala中與XML相關的包和類很多,具體如下圖所示:
在深入講解Scala操縱XML之前,先對幾個主要的術語進行介紹:
圖中描述了屬性、根元素、子元素、元素及文字的概念及它們之間的關聯關係,所以的文字、元素被統稱為節點(Node)。下面給出的scala XML中的幾個重要的類:
- Node類。它的一個抽象類,用於物件XML中的所有節點進行抽象:
- Text類,僅包含文字的節點,例如
<url>http://www.xuetuwuyou.com/</url>
中的http://www.xuetuwuyou.com/就是一種Text物件 - NodeSeq類,它同樣是一個抽象類,指的是節點的序列,Node繼承自NodeSeq,可以看Node可作是NodeSeq只有一個元素的情況。
scala中的XML中可以執行scala表示式,例如
val s="http://www.xuetuwuyou.com/"
val xmlUrl= <a>{" "+s+" "}</a>
//<a> http://www.xuetuwuyou.com/ </a>
println(xmlUrl)
val age=30
val xml1= if(age<29) <age> {age} </age> else NodeSeq.Empty
//<age> 28 </age
println(xml1)
//<age> 79 </age>
val xml2= <age> {29+50} </age>
println(xml2)
2. XML內容提取
提取XML中的文字:
object ExtractXMLText extends App{
val x= <person><name>搖擺少年夢</name><age>27</age></person>
//搖擺少年夢27
println(x.text)
}
這種提取方式將XML中所有的文字內容提取出來並拼接在一起,在實際中我們可能需要精確提取,比如我只想提取name元素中的內容,此時可以採用下列方式:
val x= <person><name>搖擺少年夢</name><age>27</age></person>
//提取name子結點,型別XPATH訪問方式
//<name>搖擺少年夢</name>
println(x \ "name")
//提取name中的文字
println((x \ "name").text)
scala> x \ "age"
res2: scala.xml.NodeSeq = NodeSeq(<age>27</age>)
x \ “age” 這種子元素的提取方式,返回的型別是scala.xml.NodeSeq
\的方式只能提取子元素,不能提取子元素的子元素,例如:
val x= <persons>
<person><name>搖擺少年夢</name><age>27</age></person>
<person><name>張三</name><age>29</age></person>
<person><name>李四</name><age>30</age></person>
</persons>
//返回空NodeSeq
println(x \ "name")
// \\提取二級子元素
//<name>搖擺少年夢</name><name>張三</name><name>李四</name>
println(x \\ "name")
通過\和\可以提取任何XML的子元素及其文字內容,但如果XML元素帶有屬性,那又如何提取呢?
val x= <persons>
<person
name="搖擺少年夢" age="27" />
<person><name>張三</name><age>29</age></person>
<person><name>李四</name><age>30</age></person>
</persons>
//用@方式提取name屬性
//搖擺少年夢
println(x \\ "@name")
3. XML物件序列化及反序列化
下面給出的是物件的XML序列化操作:
class Person(val name:String,val age:Int){
def toXML()={
<person>
<name>{name}</name>
<age>{age}</age>
</person>
}
}
object XMLSerialization extends App{
val p=new Person("搖擺少年夢",27)
println(p.toXML())
}
反序列化操作:
class Person(val name:String,val age:Int){
//序列化操作
def toXML()={
<person>
<name>{name}</name>
<age>{age}</age>
</person>
}
//反序列化操作
def fromXML(xml:scala.xml.Elem):Person={
new Person((xml \ "name").text,(xml \ "age").text.toInt)
}
override def toString()="name="+name+", age="+age
}
object XMLSerialization extends App{
val p=new Person("搖擺少年夢",27)
val xmlPerson=p.toXML()
val p2=p.fromXML(xmlPerson)
println(p2)
}
4. XML檔案讀取與儲存
前一小節,我們的序列化與反序列化操作都在記憶體中進行的,在通常的情況下都是將序列化後的XML儲存在檔案當中,在反序列化時再從檔案中讀取,實現方式如下:
class Person(val name:String,val age:Int){
def toXML()={
<person>
<name>{name}</name>
<age>{age}</age>
</person>
}
def fromXML(xml:scala.xml.Elem):Person={
new Person((xml \ "name").text,(xml \ "age").text.toInt)
}
override def toString()="name="+name+", age="+age
}
object XMLSerialization extends App{
val p=new Person("搖擺少年夢",27)
val xmlPerson=p.toXML()
//儲存到XML檔案當中
scala.xml.XML.save("person.xml", xmlPerson, "UTF-8", true, null)
//從檔案中載入XML檔案
val loadPerson=scala.xml.XML.loadFile("person.xml")
val p2=p.fromXML(loadPerson)
println(p2)
}
下面給出的是save方法的標籤
/** Saves a node to a file with given filename using given encoding
* optionally with xmldecl and doctype declaration.
*
* @param filename the filename
* @param node the xml node we want to write
* @param enc encoding to use
* @param xmlDecl if true, write xml declaration
* @param doctype if not null, write doctype declaration
*/
final def save(
filename: String,
node: Node,
enc: String = encoding,
xmlDecl: Boolean = false,
doctype: dtd.DocType = null
): Unit =
5. XML模式匹配
Scala操縱XML另外一個非常強大的地方在於,它能夠用於模式匹配,從而非常靈活、方便地對XML進行處理:
import scala.xml._
object PatternMatchingXML extends App{
def xmlMatching(node:Node)={
node match {
//XML模式匹配語法,利用{}進行匹配
case <persons>{sub_element}</persons>=> println(sub_element)
//其它未匹配的情況
case _ => println("no matching")
}
}
//下面這條語句的執行結果:<person><name>搖擺少年夢</name></person>
xmlMatching(<persons><person><name>搖擺少年夢</name></person></persons>)
//下面這條語句的執行結果:
//no matching
xmlMatching(<persons><person><name>搖擺少年夢</name></person><person><name>搖擺少年夢</name></person></persons>)
}
從上述程式碼可以看到,<persons>{sub_element}</persons>
只能匹配標籤<persons></persons>
中只存在單個子元素的情況,如果具有多個子元素,即子元素構成NodeSeq,則不能匹配,需要進行進一步處理,程式碼如下:
object PatternMatchingXML extends App{
def xmlMatching(node:Node)={
node match {
//_*的方式表示可以匹配多個子元素的情況,如果匹配
//則將匹配的內容賦值給sub_element
case <persons>{sub_element @ _*}</persons>=> println(sub_element)
case _ => println("no matching")
}
}
//下面這條語句返回的是:ArrayBuffer(<person><name>搖擺少年夢</name></person>)
//陣列中的每個元素都是Node型別
xmlMatching(<persons><person><name>搖擺少年夢</name></person></persons>)
//下面這條語句返回的是:ArrayBuffer(<person><name>搖擺少年夢</name></person>, <person><name>搖擺少年夢</name></person>)
//陣列中的每個元素都是Node型別
xmlMatching(<persons><person><name>搖擺少年夢</name></person><person><name>搖擺少年夢</name></person></persons>)
}
因為返回的是ArrayBuffer,可以通過for迴圈遍歷對XML子元素中的內容進行提取,如:
def xmlMatching2(node:Node)={
node match {
case <persons>{sub_element @ _*}</persons>=>
for(elm <- sub_element) println("getting "+(elm \ "name").text)
case _ => println("no matching")
}
}
//返回結果getting 搖擺少年夢
xmlMatching2(<persons><person><name>搖擺少年夢</name></person></persons>)
//返回結果:
//getting 搖擺少年夢
//getting 搖擺少年夢
xmlMatching2(<persons><person><name>搖擺少年夢</name></person><person><name>搖擺少年夢</name></person></persons>)
新增公眾微訊號,可以瞭解更多最新Spark、Scala相關技術資訊