Scala XML处理

来源:互联网 发布:Mac字幕太小 编辑:程序博客网 时间:2024/05/22 19:20

Scala XML处理

 

 

 

简介

  1. XML是一种半结构化数据的形式。它比单纯的字符串更为结构化,因为它把数据内容组织成了树结构。尽管如此,单纯的XML的结构化程度依然比不上编程语言的对象,因为它允许在标签之间存在自由格式的文本,并且它缺少类型系统。
  2. 任何在你需要序列化程序数据以保存到文件或通过网络运输的时候,半结构化的数据都将非常有用。你无须把结构化的数据直接“降解”为字节,而只完成到半结构化数据之间的转换即可。然后你可以使用已有的库函数实现半结构化与二进制数据直接的转换,从而节省了可以用来处理更重要问题的时间。
  3. 半结构化的数据有很多种,但XML是互联网上用的最广的。大多数操作系统上都有XML工具,大多数编程语言中也都有可用的XML库。

 

 

XML结构

  1. XML的构成包含两种基本元素,文本和标签。通常,文本是指任何字符序列。标签写法类似于<pod>,由小于号、字母数字的标签名及大于号组成。包括开始标签和结束标签两种,结束标签较开始标签在签名之前多了条斜杠,如:</pod>。
  2. 开始和结束标签必须相互匹配,就像一对括号。任何开始标签必须最终跟随同样标签名的结束标签。

不合法形式:

//不合法的XMLOne<pod> , two <pod>, three <pod> zoo

//同样不合法

<pod>Three<peas> in the </pod></peas>



合法形式:

<person><name>Bob</name><age>20</age></person>


标签的这种匹配方式,使得XML以元素嵌套的方式结构化。每一对匹配的开始和结束标签组成了一个元素,元素可以嵌套在另一个元素中。

 


扩展

  1. 开始标签可以有附加的属性。属性就是指name = value这种中间有等号的名值对。属性名本身是平凡的无结构文本,属性值则必须以双引号(“ ”)或单引号(‘ ‘)包围。例如:<pod peas=”3” string=”true”/>
  2. 对于开始标签紧跟匹配的结束标签的情况下,可以简写成只要在标签名称之后加反斜杠即可。这种标签包含了空元素。例如:<pod> Three <peas/> in the </pod>

 

 

 

XML字面量

Scala允许你在任何可以存在表达式的地方以字面量的形式键入XML。只要简单地打上开始标签并连续编写XML内容即可。编译过程会进入XML输入模式并把内容读取为XML,直到发现匹配于前面开始标签的结束标签为止:

代码示例:

val elem = <person><name>Alice</name> <age>20</age></person>

上面表达式中,elem成了类型为scala.xml.Elem值,说明它是有标签名(“name”)等,有内容(“Alice”)等的XML元素。

 

 

 

XML节点

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

Elem表示的是XML元素,比如:

val elem = <a href ="www.baidu.com">The <em>Scala</em> language</a>

Label属性产出标签名称”a”,而child对应的是后代序列(两个Text节点和一个Elem节点)

 

重要的XML类:

  • Node类:所有XML节点类的抽象超类
  • Text类:只包含文本节点。例如<name>Alice</name>的”Alice”部分为Text类。
  • NodeSeq类:保存节点序列。XML库中许多地方,在你看来应该处理单个Node的,都是处理NodeSeq的。

 

XML节点类型图:

 

 

如果你通过编程的方式构建节点,则可以使用NodeBuffer,它是ArrayBuffer[Node]的子类。

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



元素属性


属性键和值

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

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

打印结果:

http://scala-lang.org


要遍历所有属性,可以这样:

val elem = <a href="http://scala-lang.org">The Scala Language</a>for (arr <- elem.attributes)  println(arr.key+" : " + arr.value.text)

打印结果:

href : http://scala-lang.org

修改元素和属性

  • Scala中,XML节点和节点序列是不可变的。如果你想要编辑一个节点,则必须创建一个拷贝,给出需要作出的修改,然后拷贝未被修改的部分。
  • 拷贝Elem节点,使用copy方法。它有五个带名参数:lable、attributes、child、prefix和scope

 

修改属性:

val list = <ul><li>Fred</li><li>Wilma</li></ul>val list2 = list.copy(label = "ol")println(list)println(list2)


打印结果:

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


添加后代:

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


打印结果:

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



内嵌表达式

简单表达式

你不仅可以写纯粹的XML,还可以在XML字面量中使用花括号({})做转义符,执行Scala代码。

代码示例:

val elem = <person>{"Alice"+",20"}</person>println(elem)println(elem.text)

打印结果:

<person>Alice,20</person>Alice,20


要在XML字面量中包含左花括号或右花括号,连续两个即可:

val elem = <a>The Scala {{123}} Language</a>println(elem)

打印结果:

<a>The Scala {123} Language</a>


转义括号可以包括任意的Scala内容,乃至更多的XML字面量。因此,随着内嵌级别的增加,代码可以在XML与普通Scala代码之间来回切换。

代码示例:

val yearMade = 1955val elem = <a>  {if (yearMade < 2000)     <old>{yearMade}</old>  else     xml.NodeSeq.Empty}</a>


注意:

转义括号内的表达式并非一定要运行出XML节点。它可以返回任何Scala值。这种情况下,结果将被转变为字符串,然后以文本节点的形式插入:

val elem = <a> { 3 + 4 } </a>println(elem)

打印结果:

<a> 7 </a>


如果以打印方式返回节点,那么文本中所有的<,>以及&字符将被转义:

val elem = <a> { "</a> potential security hole <a>" } </a>println(elem)

打印结果:

<a> </a> potential security hole <a> </a>



如果使用低级字符串操作创建XML,或许会出现下面的问题:

val elem = "<a>" + "</a> potential security hole <a>" + "</a>"println(elem)

打印结果:

<a></a> potential security hole <a></a>


属性中的表达式

你可以用Scala表达式来计算属性值,例如:

val url = "www.baidu.com"val elem = <img src ={url}></img>println(elem)

打印结果:

<img src="www.baidu.com"></img>


 

序列化

序列化XML

XML序列化是从内部数据结构到XML的转换。基础的只需要用到XML字面量和转义括号。

 

代码示例:

object exam1 {  def main(args: Array[String]): Unit = {    //尽管Person为抽象类,但是在语法上实际上实例化了Person的匿名子类,所以可以正常工作    val person = new Person {      override val name: String = "Cindy"      override val age: Int = 24      override val salary: Double = 8000    }    //打印person信息    println(person.toXML)  }  abstract class Person {    val name: String    val age: Int    val salary: Double    //重写toString    override def toString: String = "name = " +name +" , age = " + age +" , salary = " +salary    //把这个类的示例转换为XML    def toXML =      <person>        <name>{name}</name>        <age>{age}</age>        <salary>{salary}</salary>      </person>  }}

打印结果:

<person>        <name>Cindy</name>        <age>24</age>        <salary>8000.0</salary>      </person>


拆解XML

抽取文本:通过对XML节点调用text方法,你可以取回节点内去除了所有元素标签之后的全部文本。

val per = <person><name>Alice</name><age>20</age></person>println(per.text)

打印结果:

Alice20


抽取子元素:如果你想要通过标签名找到子元素,只要调用 \ 加标签名即可。

 

<pre name="code" class="java">val per = <person><name>Alice</name><age>20</age></person>//注意:只能找到子元素(基于父元素的下一层)println(per \ "name")

打印结果:

<name>Alice</name>


你可以使用 \\ 代替 \,执行“深度搜索”寻找子元素内容

val per = <html><person><name>Alice</name><age>20</age></person></html>//打印为空,只能找到子元素(基于父元素的下一层)println(per \ "name")//打印为<name>Alice</name> ,深度遍历查找子元素println(per \\ "name")



反序列化

通过上面介绍的XML拆解方法,你可以编写序列化器的对偶部分,从XML转回你的内部数据结构的解析器。

代码示例:

object exam1 {  def main(args: Array[String]): Unit = {    //尽管Person为抽象类,但是在语法上实际上实例化了Person的匿名子类,所以可以正常工作    val person = new Person {      override val name: String = "Cindy"      override val age: Int = 24      override val salary: Double = 8000    }    //序列化person对象    val node = person.toXML    //反序列化person对象    val p = person.fromXML(node)    println(p.toString)  }  abstract class Person {    val name: String    val age: Int    val salary: Double    //重写toString    override def toString: String = "name = " +name +" , age = " + age +" , salary = " +salary    //把这个类的示例转换为XML    def toXML =       <person>        <name>{name}</name>        <age>{age}</age>        <salary>{salary}</salary>      </person>    def fromXML(node: scala.xml.Node) : Person = new Person{      override val name: String = {node \ "name"}.text      override val age: Int = {node \ "age"}.text.toInt      override val salary: Double = {node \ "salary"}.text.toDouble    }  }}

打印结果:

name = Cindy , age = 24 , salary = 8000.0



加载和保存

XML与字节流之间的转换是比较容易的,因为可以用库函数帮你完成。你只要对正确的数据调用正确的函数即可。

  • 要把XML转换为字符串,只需要调用toString方法。你也可以自己编写toString方法。但是最好还是使用库函数,那样结果XML就可以包含指定所用字符编码的指令。如果自己编写,则需要考虑字符编码问题。
  •  
  • 要把XML转换为字节文件,你可以使用XML.saveFull命令。它的五个参数为:文件名、要保存的字点、字符编码、是否在文件头写上包含字符编码的XML声明、XML的“文档类型”。

scala.xml.XML.save("person.xml",node,"UTF-8",true,null)


保存XML

代码示例:

object exam1 {  def main(args: Array[String]): Unit = {    //尽管Person为抽象类,但是在语法上实际上实例化了Person的匿名子类,所以可以正常工作    val person = new Person {      override val name: String = "Cindy"      override val age: Int = 24      override val salary: Double = 8000    }    //序列化person对象    val node = person.toXML    //保存序列化的person对象为XML文件    scala.xml.XML.save("person.xml",node,"UTF-8",true,null)  }  abstract class Person {    val name: String    val age: Int    val salary: Double    //重写toString    override def toString: String = "name = " +name +" , age = " + age +" , salary = " +salary    //把这个类的示例转换为XML    def toXML =      <person>        <name>{name}</name>        <age>{age}</age>        <salary>{salary}</salary>      </person>  }}

保存的XML文件person.xml内容:

<?xml version='1.0' encoding='UTF-8'?><person>        <name>Cindy</name>        <age>24</age>        <salary>8000.0</salary>      </person>


加载XML

加载比保存要简单,因为文件包含了所有加载器需要知道的东西。只要调用XML.loadFile和文件名即可:

val elem: Elem = scala.xml.XML.loadFile("person.xml")println(elem)

打印结果:

<person>        <name>Cindy</name>        <age>24</age>        <salary>8000.0</salary>      </person>


其他加载方式:

 

val root = XML.load(new FileInputStream("myfile.xml"))val root2 = XML.load(new InputStreamReader(new FileInputStream("myfile.xml"),("UTF-8")))val root3 = XML.load(new URL("www.baidu.com"))



 

XML模式匹配

 

XML的模式看上去很像XML字面量。主要差别在于如果你插入转义括号{},那么{}内的代码不是表达式而是模式。{}内嵌的模式可以使用所有的Scala模式语言,包括绑定新变量,执行类型检查,以及使用_和_*忽略内容。

 

代码示例(处理单节点):

println(proc(<a>apple</a>))println(proc(<b>banana</b>))println(proc(<html>page</html>)) def proc(node: scala.xml.Node): String ={  node match {      //匹配a标签的节点,这里只能匹配单个    case <a>{ contents}</a> => "It's a :" + contents      //匹配b标签的节点,这里只能匹配单个    case <b>{contents}</b> => "It's b : " + contents      //匹配除了上面两个以外的其他节点    case _ => "Something else"  }}


打印结果:

It's a :appleIt's b : bananaSomething else


代码示例(处理多节点):

def proc(node: scala.xml.Node): Unit={  node match {      //匹配a标签的节点,这里只能匹配单个    case <catalog>{ contents @ _*}</catalog> =>      for(item <- contents)         println("processing: " + item)      //匹配除了上面以外的其他节点    case _ => println("Something else")  }}

打印结果:

processing: <a>apple</a>processing: <a>animal</a>






0 0
原创粉丝点击