最近因为做一套SSO框架,需要解析SAML报文,研究了一下openSAML发现其需要1.5以上的jdk,于是决定自己做解析程序。在网上比较了一番dom和sax后,决定用sax来解析,因为我们更关注的是报文中某些个属性值,而不是报文的结构。闲话少叙,下面开始SAX的学习。
首先我们先了解一下SAX的基本概念。SAX是Simple Api for XML的缩写,相比与DOM,SAX是一种轻量级的XML访问方法。DOM在处理xml文档时,我们需要读入整个文档,在内存中创建dom树。在文档比较小的时候这没有什么问题,但随着文档的增大,dom的访问效率以及内存占用都会出现问题,这时,SAX就是比较好的替代方案。SAX不同于DOM的文档驱动,它是事件驱动的,在解析过程中,它不需要读入整个文档,文档读入的过程就是解析过程。所谓的事件驱动,是一种基于回调机制的程序运行方法。在解析之前,先注册一个ContentHandler,相当于一个事件监听器,其中定义了很多方法,用以处理在读取文档各个部分时要做的事情,比如startDocument(),它定义了解析过程中,遇到文档开始时所作的事情。当XMLReader读取到合适的内容,就会抛出相应的事件,把内容交给ContentHander去处理。在网上搜索SAX资料时,找到了一个简单的例子,很清晰的说明了SAX解析过程。我们来看这个简单的xml文件
<POEM>
<AUTHOR>Ogden Nash</AUTHOR>
<TITLE>Fleas</TITLE>
<LINE>Adam</LINE>
</POEM>
SAX的解析过程:
遇到的项目 | 方法回调 |
{文档开始} | startDocument() |
<POEM> | startElement(null,"POEM",null,{Attributes}) |
"\n" | characters("<POEM>\n...", 6, 1) |
<AUTHOR> | startElement(null,"AUTHOR",null,{Attributes}) |
"Ogden Nash" | characters("<POEM>\n...", 15, 10) |
</AUTHOR> | endElement(null,"AUTHOR",null) |
"\n" | characters("<POEM>\n...", 34, 1) |
<TITLE> | startElement(null,"TITLE",null,{Attributes}) |
"Fleas" | characters("<POEM>\n...", 42, 5) |
</TITLE> | endElement(null,"TITLE",null) |
"\n" | characters("<POEM>\n...", 55, 1) |
<LINE> | startElement(null,"LINE",null,{Attributes}) |
"Adam" | characters("<POEM>\n...", 62, 4) |
</LINE> | endElement(null,"LINE",null) |
"\n" | characters("<POEM>\n...", 67, 1) |
</POEM> | endElement(null,"POEM",null) |
{文档结束} | endDocument() |
SAX提供了一个DefaultHandler类,其中所有方法为空实现,我们要做的就是创建一个自己的Handeler类继承DefaultHandler,然后重载其中的startDocument()、startElement()、characters()、endElement()等方法来自己的处理。
现在我们来动手做个解析SAML AuthnRequest报文的例子,首先来看报文
<samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="id_1"
Version="2.0"
IssueInstant="2007-12-05T09:21:59Z">
<saml:Issuer>https://www.chinamobile.com/SSO</saml:Issuer>
<samlp:NameIDPolicy
AllowCreate="true"
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"/>
</samlp:AuthnRequest>
我们要把其中的ID、IssueInstant、Issuer、AllowCreate、Format属性解析出来,放到一个java对象中。 首先我们定义一个AuthRequest对象,其中包含上面的属性以及get、set方法
static class AuthRequest {
private String id;
private String issueInstant;
private String issuer;
private String allowCreate;
private String format;
public void setID(String id){
this.id = id;
}
public void setIssueInstant(String issueInstant){
this.issueInstant = issueInstant;
}
public void setIssuer(String issuer){
this.issuer = issuer;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getAllowCreate() {
return allowCreate;
}
public void setAllowCreate(String allowCreate) {
this.allowCreate = allowCreate;
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
public String getIssueInstant() {
return issueInstant;
}
public String getIssuer() {
return issuer;
}
@Override
public String toString() {
return "id="+id+",issueInstant="+issueInstant+",issuer="+issuer;
}
}
然后我们创建自己的handler MyHandler
static class MyHandler extends DefaultHandler {
private AuthRequest request;
private Stack stack;
public MyHandler(AuthRequest request){
this.request = request;
this.stack = new Stack();
}
@Override
public void startElement(String uri, String localName, String name,
Attributes attributes) throws SAXException {
stack.push(name);
if ("samlp:AuthnRequest".equalsIgnoreCase(name)){
for (int i = 0;i < attributes.getLength();i ++){
if ("ID".equalsIgnoreCase(attributes.getQName(i))){
request.setID(attributes.getValue(i));
}else if ("IssueInstant".equalsIgnoreCase(attributes.getQName(i))){
request.setIssueInstant(attributes.getValue(i));
}
}
}else if ("samlp:NameIDPolicy".equalsIgnoreCase(name)){
for (int i = 0;i < attributes.getLength();i ++){
if ("AllowCreate".equalsIgnoreCase(attributes.getQName(i))){
request.setAllowCreate(attributes.getValue(i));
}else if ("Format".equalsIgnoreCase(attributes.getQName(i))){
request.setFormat(attributes.getValue(i));
}
}
}
}
@Override
public void endElement(String uri, String localName, String name)
throws SAXException {
stack.pop();
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
if ("saml:Issuer".equalsIgnoreCase((String)stack.peek())){
request.setIssuer(new String(ch,start,length));
}
}
}
这里我们用了一个stack来保存当前解析的节点信息。因为标准的xml每个标签都一定有一个对应的结束标签,我们在startElement时把标签名称push到stack中,在endElement时pop出来,这样就可以获取当前解析的标签在文档中的位置。
最后我们来执行代码
public static void main(String[] args) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
AuthRequest request = new AuthRequest();
parser.parse(new File("src/xml/req.xml"), new MyHandler(request));
System.out.println(request);
}
得到输出:
id=id_1,issueInstant=2007-12-05T09:21:59Z,issuer=https://www.chinamobile.com/SSO
这里我们完成了一个简单的将XML转成JAVA对象的例子。可以看出,SAX提供的是一套比DOM更为基础的XML解析API,通过自定义Handler,我们可以自己控制xml解析过程,只解析我们关心的信息。当然具体用DOM,还是SAX,还是要根据实际情况去选择。
分享到:
相关推荐
XML基础及DOM 文档对象模式和SAX 基于事件处理模式 学习笔记
android 学习笔记1--SAX解析XML,了解下如何用sax解析xml吧
java学习笔记——使用DOM解析XML和使用SAX解析XML
整理了四种常用的Java操作xml的方法 学习java操作xml的好资料
{14.3.1}SAX应用}{206}{subsection.14.3.1} {14.4}dom4j}{207}{section.14.4} {14.5}XPath}{210}{section.14.5} {14.6}apache.commons}{211}{section.14.6} {15}sqlite3}{213}{chapter.15} {16}Web基础}{215}{...
首先Digester是什么,它是用来解析xml文件的的工具,是jakarta开源项目下commons的一个子项目,它能让程序员更方便的解析xml文件,而不需要了解底层的工作细节。 如果要使用Digester作为xml文件的解析,请到jakarta...
61. 使用 SAX 处理 XML 62. 保存 XML 63. 使用 QJson 处理 JSON 64. 使用 QJsonDocument 处理 JSON 65. 访问网络(1) 66. 访问网络(2) 67. 访问网络(3) 68. 访问网络(4) 69. 进程 70. 进程间通信 71. 线程...
本文档详细的给出了XML的介绍和XML解析的实例。包括DOM4J和SAX解析,节点的名和值得读取,属性的读取。生成XML文件等
Python 的 DOM 包是基于 SAX 构建的,并且包括在 Python 2.0 的标准 XML 支持里。 一、xml.dom的简单介绍 1、主要方法: minidom.parse(filename):加载读取XML文件doc.documentElement:获取XML文档对象node....
学习xml时候的笔记以及心得 1)DOM(JAXP Crimson解析器) 2)SAX 3)JDOM http://www.jdom.org 4)DOM4J http://dom4j.sourceforge.net
CVPR 2018笔记以下是我在大会期间遇到的最有趣的论文,挑战和研讨会的链接,注释和想法。 但是,您可能希望通过回顾所有979篇被接受的论文并逐步浏览所有21个教程和48个研讨会的内容,来形成自己对计算机视觉和模式...