半结构化数据
-
XML是一种半结构化数据,既不是纯文本数据也不是编程中使用到的数据结构。在保存数据到文件中或对文件进行网络传输的时候非常有用,将数据转换为半结构数据,然后使用库中的工具将半结构数据转换为二进制数据。
XML简介
-
XML的组成元素为Tag和Text,Tag之间是不能交叉的,可以嵌套。Tag可以有自己的属性,属性就是一个name-value对,name是plain的,value值被“”或者‘’包裹起来。
XML字面量
-
Scala支持XML字面量,XML字面量是一个表达式,可用在任意表达式可使用的地方,类型是Scala.xml.Elem,是一个XML元素,其余的重要的类有:Node,是XML所有节点类的抽象总类;Text,只包含有文本的Node, -
NodeSeq类含有Node序列,许多XML的库都支持对NodeSeq的操作,Node继承自NodeSeq。
构造XML字面量的时候可以在其中嵌入Scala表达式,使用{}包括起来。如果在表达式中插入Tag,直接写入即可。直接写入<old></old>标签,空节点使用XML.NodeSeq.Empty表达。scala> <a> {if (yearMade < 2000) <old>{yearMade}</old> else xml.NodeSeq.Empty} </a> scala> <a> {"</a>potential security hole<a>"} </a> res4: scala.xml.Elem = <a> </a>potential security hole<a> </a>
其中<,>和$会被转义成为对应的字符实体。如果使用单纯的字符串拼接,则无法阻止用户对XML的修改,会造成错误的情况发生。
scala> "<a>" + "</a>potential security hole<a>" + "</a>" res5: String = <a></a>potential security hole<a></a>
所以最好还是使用XML字面量来生成XML Elem。
序列化
- 第一种序列化:在内部的数据结构中定义一个
toXML方法,使用XML字面量手动构建XML。
拆分XML
- 方法基于
XPath语言,可以直接在Scala中使用。使用text方法可以直接提取到XML节点之间的文字,其中的<,>和$可以被自动的转换。
使用\可以提取XML中的子节点。
使用\\可以提取任意深度的子节点。scala> <a><b><c>hello</c></b></a> \\"c" res12: scala.xml.NodeSeq = NodeSeq(<c>hello</c>)
同样可以使用这两个方法来提取属性
scala> val joe = <employee name="Joe" rank="code monkey" serial="123"/> joe: scala.xml.Elem = <employee name="Joe" rank="code monkey" serial="123"/> scala> joe \\ "@name" res15: scala.xml.NodeSeq = Joe
"@name",@是在被提取的字符串里面的。
反序列化
- 通过
\\可以对一个类定义parser来解析相应的XML,将其反序列化为相应的数据结构。
加载和保存
- 序列化的最后一步是
XML和字节之间的转换,这部分一般都有相应的库函数来做。将XML转为String,调用toString即可,但还是推荐使用scala.xml.XML.save("therm1.xml", node)函数将XML转为文件进行保存,同时可以指定文件的编码。导入更简单,使用val loadnode = xml.XML.loadFile("therm1.xml")
XML中的模式匹配
- 以上的例子都是在非常确定
XML结构的情况下做的序列化和反序列化,如果一个XML的结构是不确定的,则使用模式匹配来筛选可能性。用于匹配的模式非常像XML字面量,在其中也可以嵌入使用{}包裹的表达式,但{}`中的表达式不再计算,而是一个可以被使用的模式,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." }
其中a和b只能匹配只有一个节点的XML,例如<a>apple</a>,<b><c/></b>此类可以,标签中间只能有一个节点,不能有多的节点。<a>scala<b/></a>这样是不行的。如果要使提取出来这种XML序列,使用_*进行匹配,可以使用变量绑定,将_*的值赋值到contents上方便以后使用。
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." }
在XML中,换行或者空格等空白字符也都是一个节点,