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文件
Xml代码 复制代码  收藏代码
  1. <POEM>  
  2. <AUTHOR>Ogden Nash</AUTHOR>  
  3. <TITLE>Fleas</TITLE>  
  4. <LINE>Adam</LINE>  
  5. </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报文的例子,首先来看报文
Xml代码 复制代码  收藏代码
  1. <samlp:AuthnRequest  
  2.     xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"  
  3.     xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"  
  4.     ID="id_1"  
  5.     Version="2.0"  
  6.     IssueInstant="2007-12-05T09:21:59Z">  
  7.     <saml:Issuer>https://www.chinamobile.com/SSO</saml:Issuer>  
  8.     <samlp:NameIDPolicy  
  9.         AllowCreate="true"  
  10.         Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"/>  
  11. </samlp:AuthnRequest>  


我们要把其中的ID、IssueInstant、Issuer、AllowCreate、Format属性解析出来,放到一个java对象中。 首先我们定义一个AuthRequest对象,其中包含上面的属性以及get、set方法

Java代码 复制代码  收藏代码
  1.   
  2. static class AuthRequest {   
  3.     private String id;   
  4.     private String issueInstant;   
  5.     private String issuer;   
  6.     private String allowCreate;   
  7.     private String format;   
  8.     public void setID(String id){   
  9.         this.id = id;   
  10.     }   
  11.     public void setIssueInstant(String issueInstant){   
  12.         this.issueInstant = issueInstant;   
  13.     }   
  14.     public void setIssuer(String issuer){   
  15.         this.issuer = issuer;   
  16.     }   
  17.     public String getId() {   
  18.         return id;   
  19.     }   
  20.     public void setId(String id) {   
  21.         this.id = id;   
  22.     }   
  23.     public String getAllowCreate() {   
  24.         return allowCreate;   
  25.     }   
  26.     public void setAllowCreate(String allowCreate) {   
  27.         this.allowCreate = allowCreate;   
  28.     }   
  29.     public String getFormat() {   
  30.         return format;   
  31.     }   
  32.     public void setFormat(String format) {   
  33.         this.format = format;   
  34.     }   
  35.     public String getIssueInstant() {   
  36.         return issueInstant;   
  37.     }   
  38.     public String getIssuer() {   
  39.         return issuer;   
  40.     }   
  41.     @Override  
  42.     public String toString() {   
  43.         return "id="+id+",issueInstant="+issueInstant+",issuer="+issuer;   
  44.     }   
  45. }  
	
	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

Java代码 复制代码  收藏代码
  1. static class MyHandler extends DefaultHandler {   
  2.        
  3.     private AuthRequest request;   
  4.     private Stack stack;   
  5.        
  6.     public MyHandler(AuthRequest request){   
  7.         this.request = request;   
  8.         this.stack = new Stack();   
  9.     }   
  10.   
  11.     @Override  
  12.     public void startElement(String uri, String localName, String name,   
  13.             Attributes attributes) throws SAXException {   
  14.         stack.push(name);   
  15.         if ("samlp:AuthnRequest".equalsIgnoreCase(name)){   
  16.             for (int i = 0;i < attributes.getLength();i ++){   
  17.                 if ("ID".equalsIgnoreCase(attributes.getQName(i))){   
  18.                     request.setID(attributes.getValue(i));   
  19.                 }else if ("IssueInstant".equalsIgnoreCase(attributes.getQName(i))){   
  20.                     request.setIssueInstant(attributes.getValue(i));   
  21.                 }   
  22.             }   
  23.         }else if ("samlp:NameIDPolicy".equalsIgnoreCase(name)){   
  24.             for (int i = 0;i < attributes.getLength();i ++){   
  25.                 if ("AllowCreate".equalsIgnoreCase(attributes.getQName(i))){   
  26.                     request.setAllowCreate(attributes.getValue(i));   
  27.                 }else if ("Format".equalsIgnoreCase(attributes.getQName(i))){   
  28.                     request.setFormat(attributes.getValue(i));   
  29.                 }   
  30.             }   
  31.         }   
  32.            
  33.     }   
  34.        
  35.     @Override  
  36.     public void endElement(String uri, String localName, String name)   
  37.             throws SAXException {   
  38.         stack.pop();   
  39.     }   
  40.        
  41.     @Override  
  42.     public void characters(char[] ch, int start, int length)   
  43.             throws SAXException {   
  44.         if ("saml:Issuer".equalsIgnoreCase((String)stack.peek())){   
  45.             request.setIssuer(new String(ch,start,length));   
  46.         }   
  47.     }   
  48. }  
	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出来,这样就可以获取当前解析的标签在文档中的位置。
最后我们来执行代码
Java代码 复制代码  收藏代码
  1. public static void main(String[] args) throws Exception {   
  2.     SAXParserFactory  factory = SAXParserFactory.newInstance();   
  3.     SAXParser parser = factory.newSAXParser();   
  4.     AuthRequest request = new AuthRequest();   
  5.     parser.parse(new File("src/xml/req.xml"), new MyHandler(request));   
  6.     System.out.println(request);   
  7. }  
	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,还是要根据实际情况去选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值