1. 程式人生 > >Scala入門到精通——第二十七節 Scala操縱XML

Scala入門到精通——第二十七節 Scala操縱XML

本節主要內容

  1. XML 字面量
  2. XML內容提取
  3. XML物件序列化及反序列化
  4. XML檔案讀取與儲存
  5. 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相關技術資訊
這裡寫圖片描述