XML相关
Posted dabokele
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XML相关相关的知识,希望对你有一定的参考价值。
本章介绍Scala对XML的支持,包括的主要内容参考本文一级标题。
有关半结构化数据的概念和XML格式的说明就跳过了,直接进入Scala中XML相关的语法和使用。
一、XML语法
在Scala命令行中可以直接输入以个Tag开头,编译器会自动进入XML输入模式,直到遇到匹配该Tag的结束标志为止。如下所示:
scala> <a>
| This is some XML.
| Here is a tag: <atag/>
| </a>
res25: scala.xml.Elem =
<a>
This is some XML.
Here is a tag: <atag/>
</a>
上面代码中得到的是一个scala.xml.Elem
类型的对象。在Scala中,XML相关的其他类还有:
Node
XML相关的所有node类的父类
Text
一个只包含XML值的node。比如<a>stuff</a>
中的stuff
部分。
NodeSeq
node序列
除了像上图中直接输入完整的XML标签和值之外,还可以在值部分以来动态计算值部分。如下图所示,
scala> <a> "hello"+", world" </a>
res26: scala.xml.Elem = <a> hello, world </a>
上面代码中中间是一个字符串拼接表达式,在
中可以包含任意形式的Scala表达式,比如
if... else…
等,也可以访问在定义的变量。
二、序列化
这里的序列化是指,将一个对象转换成XML形式。与后面的反序列化相对应。
如下所示代码定义了一个CCTherm
抽象类。
abstract class CCTherm
val description: String
val yearMade: Int
val dateObtained: String
val bookPrice: Int // in US cents
val purchasePrice: Int // in US cents
val condition: Int // 1 to 10
override def toString = description
def toXML =
<cctherm>
<description>description</description>
<yearMade>yearMade</yearMade>
<dateObtained>dateObtained</dateObtained>
<bookPrice>bookPrice</bookPrice>
<purchasePrice>purchasePrice</purchasePrice>
<condition>condition</condition>
</cctherm>
上面代码中toXML
方法可以根据CCTherm
类的属性将其转化成一个scala.xml.Elem
类型的变量。
下面代码构造一个CCTherm
类型的对象并调用toXML
方法对类进行XML转化。
val therm = new CCTherm
val description = "hot dog #5"
val yearMade = 1952
val dateObtained = "March 14, 2006"
val bookPrice = 2199
val purchasePrice = 500
val condition = 9
println(therm)
println(therm.toXML)
结果如下:
hot dog #5
<cctherm>
<description>hot dog #5</description>
<yearMade>1952</yearMade>
<dateObtained>March 14, 2006</dateObtained>
<bookPrice>2199</bookPrice>
<purchasePrice>500</purchasePrice>
<condition>9</condition>
</cctherm>
最后,如果想要在XML的内容中输入或者
,需要同时输入两个,如下所示:
scala> <a> brace yourself! </a>
res27: scala.xml.Elem = <a> brace yourself! </a>
三、XML数据访问
在XML类中提供的多个方法中,有三个方法可以用来对XML进行解析,直接获取其中的部分信息。
1、text
方法,获取XML node中的值部分
scala> <a>Sounds <tag/> good</a>.text
res28: String = Sounds good
2、\\
方法和\\\\
方法,获取XML中的子元素
(1)\\
方法
scala> <a><b><c>hello</c></b></a> \\ "b"
res4: scala.xml.NodeSeq = NodeSeq(<b><c>hello</c></b>)
scala> <a><b><c>hello</c></b></a> \\ "c"
res5: scala.xml.NodeSeq = NodeSeq()
scala> <a><b><c>hello</c></b></a> \\ "b" \\ "c"
res6: scala.xml.NodeSeq = NodeSeq(<c>hello</c>)
scala> <a><b><c>hello</c></b></a>.text
res7: String = hello
从上图可以看到,\\
方法返回的是一个NodeSeq
类型的对象,哪怕指定的子元素只有一个。
上图中还可以看到,text
方法在嵌套XML中也能直接获取其中的值部分。
(2)\\\\
方法
上面\\
方法只能获取下一层的子元素,比如,直接访问子元素”c”时,得到的是空的NodeSeq。如果想要直接访问”c”元素并得到对应的子元素,那么就需要使用\\\\
方法了。
scala> <a><b><c>hello</c></b></a> \\ "c"
res8: scala.xml.NodeSeq = NodeSeq()
scala> <a><b><c>hello</c></b></a> \\\\ "c"
res9: scala.xml.NodeSeq = NodeSeq(<c>hello</c>)
scala> <a><b><c>hello</c></b></a> \\ "a"
res10: scala.xml.NodeSeq = NodeSeq()
scala> <a><b><c>hello</c></b></a> \\\\ "a"
res11: scala.xml.NodeSeq = NodeSeq(<a><b><c>hello</c></b></a>)
3、@
方法,获取标签的属性值
在XML的标签中,可以指定一些标签属性。不带属性的标签如<a/>
,带属性的标签如<a name=“x” age=“12”/>。使用
@方法就可以获取到”name”, “age”部分的值。
@
符号需要与
`或\\\\
方法配合使用。
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"
res12: scala.xml.NodeSeq = Joe
scala> joe \\ "@serial"
res13: scala.xml.NodeSeq = 123
scala> joe \\ "@x"
res14: scala.xml.NodeSeq = NodeSeq()
该方法返回的也是NodeSeq
类型的变量。当访问不存在的属性时,得到的是空NodeSeq。
四、反序列化
反序列化和前面介绍的序列化过程正好相反,是用于将一个XML元素转化成普通对象的过程。结合上面提到的三个方法,就可以方便的获取指定元素的值,从而将XML转化成普通对象。
继续在前面的CCTherm
类中添加fromXML
方法,如下所示:
def fromXML(node: scala.xml.Node): CCTherm =
new CCTherm
val description = (node \\ "description").text
val yearMade = (node \\ "yearMade").text.toInt
val dateObtained = (node \\ "dateObtained").text
val bookPrice = (node \\ "bookPrice").text.toInt
val purchasePrice = (node \\ "purchasePrice").text.toInt
val condition = (node \\ "condition").text.toInt
五、加载和保存
从前面命令行模式可以看到,XML类型自带的toString
方法可以将其中的内容进行String转换。
但是最好还是使用XML提供的一些方法手动将XML内容转化成字符串形式进行输出。因为这种输出会包含一些属性,比如字符编码等。
1、scala.xml.XML.save
方法,将XML内容保存到指定文件中
用法如下scala.xml.XML.save(“therm1.xml”, node)。执行后,打开
therm1.xml`,其中内容如下:
<?xml version='1.0' encoding='UTF-8'?>
<cctherm>
<description>hot dog #5</description>
<yearMade>1952</yearMade>
<dateObtained>March 14, 2006</dateObtained>
<bookPrice>2199</bookPrice>
<purchasePrice>500</purchasePrice>
<condition>9</condition>
</cctherm>
2、xml.XML.loadFile
方法,将XML文件直接加载到scala.xml.Elem
对象中
如下所示val loadnode = xml.XML.loadFile("therm1.xml”)
,运行结果如下:
<cctherm>
<description>hot dog #5</description>
<yearMade>1952</yearMade>
<dateObtained>March 14, 2006</dateObtained>
<bookPrice>2199</bookPrice>
<purchasePrice>500</purchasePrice>
<condition>9</condition>
</cctherm>
六、XML中的模式匹配
到目前为止,已经知道了如何生存XML类型对象,以及如何从XML中取出指定元素的值了。但是上面介绍的那些方法只有在你明确知道XML文件的格式,并且知道自己取出来的内容结构时才好用。
1、模式匹配简单应用
XML中的模式匹配和XML表达式很类似,唯一的区别就是,在XML表达式中中的内容是scala代码,但是在模式匹配中
中的内容是一个匹配的模式。
示例如下:
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."
运行结果如下:
scala> proc(<a>apple</a>)
res15: String = It's an a: apple
scala> proc(<b>banana</b>)
res16: String = It's a b: banana
scala> proc(<c>cherry</c>)
res17: String = It's something else.
但是上面的模式匹配,只能处理一层的XML对象,对于嵌套的XML,将无法解析,如下所示:
scala> proc(<a>a <em>red</em> apple</a>)
res18: String = It's something else.
scala> proc(<a/>)
res19: String = It's something else.
2、模式匹配高级应用
我们通常会希望XML模式匹配的功能不止如此简单,最好也能匹配到嵌套的XML元素。嵌套XML中取出的是XML node序列。在模式匹配中应该写成_*
的形式。
改进后的proc
方法如下所示:
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."
在代码中有一个@
符号在_*
之前,这个是Scala模式匹配中提到过的变量绑定,通过@
符号,可以将匹配到的内容绑定到contents
变量上,在后续代码中就可以直接使用该变量。
运行结果如下
scala> proc(<a>a <em>red</em> apple</a>)
res20: String = It's an a: ArrayBuffer(a , <em>red</em>, apple)
scala> proc(<a/>)
res21: String = It's an a: WrappedArray()
模式匹配能够更好的进行XML解析,看如下这个示例:
val catalog =
<catalog>
<cctherm>
<description>hot dog #5</description>
<yearMade>1952</yearMade>
<dateObtained>March 14, 2006</dateObtained>
<bookPrice>2199</bookPrice>
<purchasePrice>500</purchasePrice>
<condition>9</condition>
</cctherm>
<cctherm>
<description>Sprite Boy</description>
<yearMade>1964</yearMade>
<dateObtained>April 28, 2003</dateObtained>
<bookPrice>1695</bookPrice>
<purchasePrice>595</purchasePrice>
<condition>5</condition>
</cctherm>
</catalog>
从代码中看到在<catalog>
标签下有两个<cctherm>
子元素。但是打印出来后的结果如下。这是由于在标签<catalog>
之后,里面包含的内容都是它的子元素,我们可以看到在最该标签的开始和结束各有一个换行,并且在两个<cctherm>
之间也有换行符的存在。
scala> catalog match
| case <catalog>therms @ _*</catalog> =>
| for (therm <- therms)
| println("processing: "+
| (therm \\ "description").text)
|
processing:
processing: hot dog #5
processing:
processing: Sprite Boy
processing:
从结果可以看到在每个<catalog>
标签前后都有一个空的元素。使用下面这段代码对XML进行处理,就可以跳过这些空元素了。
scala> catalog match
| case <catalog>therms @ _*</catalog> =>
| for (therm @ <cctherm>_*</cctherm> <- therms)
| println("processing: "+
| (therm \\ "description").text)
|
processing: hot dog #5
processing: Sprite Boy
以上是关于XML相关的主要内容,如果未能解决你的问题,请参考以下文章