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相关技术资讯

时间: 2024-10-01 12:38:20

Scala入门到精通——第二十七节 Scala操纵XML的相关文章

Scala入门到精通——第二十节 类型参数(二)

本节主要内容 Ordering与Ordered特质 上下文界定(Context Bound) 多重界定 类型约束 1. Ordering与Ordered特质 在介绍上下文界定之前,我们对scala中的Ordering与Ordered之间的关联与区别进行讲解,先看Ordering.Ordered的类继承层次体系: 通过上面两个图可以看到,Ordering混入了java中的Comparator接口,而Ordered混入了java的Comparable接口,我们知道java中的Comparator是一

Scala入门到精通——第二十一节 类型参数(三)-协变与逆变

作者:摇摆少年梦 视频地址:http://www.xuetuwuyou.com/course/12 本节主要内容 协变 逆变 类型通匹符 1. 协变 协变定义形式如:trait List[+T] {} .当类型S是类型A的子类型时,则List[S]也可以认为是List[A}的子类型,即List[S]可以泛化为List[A].也就是被参数化类型的泛化方向与参数类型的方向是一致的,所以称为协变(covariance). 图1 协变示意图 为方便大家理解,我们先分析java语言中为什么不存在协变及下一

Scala入门到精通——第十节 Scala类层次结构、Traits初步

本节主要内容 Scala类层次结构总览 Scala中原生类型的实现方式解析 Nothing.Null类型解析 Traits简介 Traits几种不同使用方式 1 Scala类层次结构 Scala中的类层次结构图如下: 来源:Programming in Scala 从上面的类层次结构图中可以看到,处于继承层次最顶层的是Any类,它是scala继承的根类,scala中所有的类都是它的子类 Any类中定义了下面几个方法: //==与!=被声明为final,它们不能被子类重写 final def ==

Scala入门到精通——第二十八节 Scala与JAVA互操作

本节主要内容 JAVA中调用Scala类 Scala中调用JAVA类 Scala类型参数与JAVA泛型互操作 Scala与Java间的异常处理互操作 1. JAVA中调用Scala类 Java可以直接操作纵Scala类,如同Scala直接使用Java中的类一样,例如: //在Person.scala文件中定义Scala语法的Person类 package cn.scala.xtwy.scalaToJava class Person(val name:String,val age:Int) //伴

Scala入门到精通—— 第二节Scala基本类型及操作、程序控制结构

本节主要内容 Scala基本类型 基本类型操作 Scala程序控制结构 Scala基本类型 Scala中的基本数据类型如下图: (来源:Programming in Scala) 从上表中可以看出,Scala的基本数据类型与Java中的基本数据类型是一一对应的,不同的是Scala的基本数据类型头字母必须大写,本节以Int.Long.String.Char.Double及Float类型为例介绍Scala的基本类型定义 整数类型变量定义: //16进制定义法 scala> val x=0x29 x:

Scala入门到精通——第二十四节 高级类型 (三)

作者:摆摆少年梦 视频地址:http://blog.csdn.net/wsscy2004/article/details/38440247 本节主要内容 Type Specialization Manifest.TypeTag.ClassTag Scala类型系统总结 在scala中,类(class)与类型(type)是两个不一样的概念.我们知道类是对同一类型数据的抽象,而类型则更具体.比如定义class List[T] {}, 可以有List[Int] 和 List[String]等具体类型,

Scala入门到精通——第二十五节 提取器(Extractor)

作者:摇摆少年梦 视频地址:http://www.xuetuwuyou.com/course/12 本节主要内容 apply与unapply方法 零变量或变量的模式匹配 提取器与序列模式 scala中的占位符使用总结 1. apply与unapply方法 apply方法我们已经非常熟悉了,它帮助我们无需new操作就可以创建对象,而unapply方法则用于析构出对象,在模式匹配中特别提到,如果一个类要能够应用于模式匹配当中,必须将类声明为case class,因为一旦被定义为case class,

Scala入门到精通——第二十九节 Scala数据库编程

本节主要内容 Scala Maven工程的创建 Scala JDBC方式访问MySQL Slick简介 Slick数据库编程实战 SQL与Slick相互转换 本课程在多数内容是在官方教程上修改而来的,官方给的例子是H2数据库上的,经过本人改造,用在MySQL数据库上,官方教程地址:http://slick.typesafe.com/doc/2.1.0/sql-to-slick.html 1. Scala Maven工程的创建 本节的工程项目采用的是Maven Project,在POM.xml文件

Scala入门到精通——第七节:类和对象(二)

本节主要内容 单例对象 伴生对象与伴生类 apply方法 应用程序对象 抽象类 单例对象 在某些应用场景下,我们可能不需要创建对象,而是想直接调用方法,但是Scala语言并不支持静态成员,Scala通过单例对象来解决该问题.单例对象的创建方式如下: object Student { private var studentNo:Int=0; def uniqueStudentNo()={ studentNo+=1 studentNo } def main(args: Array[String]):