SAX学习笔记

最近因为做一套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的解析过程:

[table]
|遇到的项目 | 方法回调
|{文档开始} | 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()
[/table]

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);
}

得到输出:[i]id=id_1,issueInstant=2007-12-05T09:21:59Z,issuer=https://www.chinamobile.com/SSO[/i]

这里我们完成了一个简单的将XML转成JAVA对象的例子。可以看出,SAX提供的是一套比DOM更为基础的XML解析API,通过自定义Handler,我们可以自己控制xml解析过程,只解析我们关心的信息。当然具体用DOM,还是SAX,还是要根据实际情况去选择。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值