Chapter 28 《Working with XML》
2018-06-11 本文已影响1人
liqing151
XML简介
-
XML
是半结构化的数据形式,在序列化程序数据用以在网络中进行传输的过程中非常有用。不是直接将程序中的结构化数据变为二进制数据,而是在结构化数据和半结构化数据的XML
形式之间进行转化。然后使用预先定义的库在半结构化数据和二进制数据之间进行转换。
-
-
XML
中的标签是嵌套的,是栈的形式。
-
- 在
Scala
中可以直接以字面量的形式写XML
文件。
- 在
-
Scala
中关于XML
的主要类有Elem
,Node
:所有XML
节点类的抽象父类;Text
:仅仅含有文字的节点;NodeSeq
:处理节点的序列,在某些地方可能需要使用处理节点序列的方法来处理单个节点,这是可以的,Node
继承自NodeSeq
。
-
- 在
XML
字面量中,可以使用{}
进行转义来求解Scala
代码的值。在{}
不仅可以使用Scala code
,也可以使用XML
字面量来完成Node
的嵌套。xml.NodeSeq.Empty
表示为空,nothing
,什么都没有的XML
节点。在{}
的<>
以及&
字符都会被转义。
- 在
scala> <a> {"</a>potential security hole<a>"} </a> res5: scala.xml.Elem = <a> </a>potential security hole<a> </a>
- 不要使用
String append
的方式来构建XML
,例如
- 不要使用
scala> "<a>" + "</a>potential security hole<a>" + "</a>" res5: String = <a></a>potential security hole<a></a>
- 程序员允许了用户在String中引入
</a>
和<a>
来改变XML
的结构,因此是不推荐使用添加字符串的形式来进行XML
的扩建。
- 程序员允许了用户在String中引入
序列化
- 从程序中的结构化数据到
XML
的序列化,只需要使用XML
字面量和{}
进行转义计算表达式的值。如果需要在{}
中包含{}
,则输入两个{}
即可。
def toXML = <cctherm> <description>{description}</description> <yearMade>{yearMade}</yearMade> <dateObtained>{dateObtained}</dateObtained> <bookPrice>{bookPrice}</bookPrice> <purchasePrice>{purchasePrice}</purchasePrice> <condition>{condition}</condition> </cctherm>
scala> <a> {{{{brace yourself!}}}} </a> res7: scala.xml.Elem = <a> {{brace yourself!}} </a>
反序列化
- Scala中使用
XPath
类似的语法来提取XML
中的各个部分。
提取tag之间的文字
elem.text
scala> <a> input ---> output </a>.text res9: String = " input ---> output "
提取子元素
使用\
方法,使用\\
方法可以进行递归的子元素搜索。
scala> <a><b><c>hello</c></b></a> \"a" res13: scala.xml.NodeSeq = NodeSeq() scala> <a><b><c>hello</c></b></a> \\"a" res14: scala.xml.NodeSeq = NodeSeq(<a><b><c>hello</c></b></a>) //是可以包含自身
提取属性
使用\
方法和\\
方法,加入@
符号
scala> val joe = <employee name="Joe" rank="code monkey" serial="123"/> scala> joe \"@name" res17: scala.xml.NodeSeq = Joe
反序列化
从上面的提取每个部分元素的方法对XML
进行反序列化生成结构化的数据。
XML和字节流之间的转换
- 最后一个转换:
XML
数据和字节流之间的转换。
- 最后一个转换:
- 最好使用库来进行
XML
的转换,可以确定字符编码,否则需要自己追踪字符的编码。
- 最好使用库来进行
将XML转换为字节数据:
scala.xml.XML.save("therm1.xml", node)
从字节数据中获取数据生成XML
:
val loadnode = xml.XML.loadFile("therm1.xml") loadnode: scala.xml.Elem = ……
- 这些都是基础的方法,还有许多针对不同读写流和输入输出流来序列化和反序列化
XML
的方法。
模式匹配
- 对
XML
使用模式匹配:模式看起来就像是XML
字面量。最主要的区别是在{}
中插入的代码不是一个表达式而是一个模式。
匹配单个节点
def proc(node: scala.xml.Node): String = node match { case <a>{contents}</a> => "It's an a: " + contents //这里的<a>与content之间的空格或者换行在node中有需要有强制性的匹配 case <b>{contents}</b> => "It's a b: " + contents case _ => "It's something else." } 可以匹配的XML为<a>It's a comment</a>,不能匹配的为<a><em>It's a comment</em></a>
匹配一个节点序列,而不是单个节点
def proc(node: scala.xml.Node): String = node match { case <a>{contents @ _*}</a> => "It's an a: " + contents case <b>{contents @ _*}</b> => "It's a b: " + contents case _ => "It's something else." }
多次使用模式匹配,跳过空白字符
- 使用变量绑定,将
_*
中的内容绑定到contents
上。模式匹配可以用来跳过XML
中的空白字符。
val catalog = <catalog> //空白字符 </cctherm> …… </cctherm> <cctherm> …… </cctherm> </catalog> // catalog中共有5个子节点,有3个空白字符节点,两个cctherm节点
// 没有处理<catalog></catalog>前后的空白字符 catalog match { case <catalog>{therms @ _*}</catalog> => for (therm <- therms) println("processing: " + (therm \"description").text) } //进一步使用模式匹配<cctherm>{_*}</cctherm生成for循环的迭代器。处理了空白字符 catalog match { case <catalog>{therms @ _*}</catalog> => for (therm @ <cctherm>{_*}</cctherm> <- therms) println("processing: " + (therm \"description").text) }