Matthew Note

用APR解析XML文件

Apache portable runtime(APR)

Apache server中一个非常关键的部分,他剥离了Apache server对于OS的依赖,使得它更容易在不同系统中迁移,另外他实现了垃圾的自动回收,解决了C/C++程序编写中非常头疼的问题。

这个APR中自带了大量的库文件,XML Parser便是其中一个。

定义

使用XMLParser需要引用apr_xml.h头文件,文件中定义了基本数据类型,和部分函数。主要的有:

  • apr_xml_attr
  • apr_xml_elem
  • apr_text_header
  • apr_text
  • apr_xml_doc

他们的定义如下,看清楚这些结构体有助于更好的使用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
typedef struct apr_text apr_text;
struct apr_text {
const char *text;
struct apr_text *next;
};
typedef struct apr_text_header apr_text_header;
struct apr_text_header {
apr_text *first;
apr_text *last;
};
typedef struct apr_xml_attr apr_xml_attr;
typedef struct apr_xml_elem apr_xml_elem;
typedef struct apr_xml_doc apr_xml_doc;
struct apr_xml_attr {
const char *name;
int ns;
const char *value;
struct apr_xml_attr *next;
};
struct apr_xml_elem {
const char *name;
int ns;
const char *lang;
apr_text_header first_cdata;
apr_text_header following_cdata;
struct apr_xml_elem *parent;
struct apr_xml_elem *next;
struct apr_xml_elem *first_child;
struct apr_xml_attr *attr;
/* used only during parsing */
struct apr_xml_elem *last_child;
struct apr_xml_ns_scope *ns_scope;
/* used by modules during request processing */
void *priv;
};
#define APR_XML_ELEM_IS_EMPTY(e) ((e)->first_child == NULL && \
(e)->first_cdata.first == NULL)
struct apr_xml_doc {
apr_xml_elem *root;
apr_array_header_t *namespaces;
};

所有关于xml的数据都存储在apr_xml_doc中,这里记录了他的子节点,父节点,每个节点的名称,属性和cdata(也就是value),很好的处理好这个结构体,就可以轻松的得到所有你想要的内容。

怎么使用

对于解析SOAP非常简单,只需调用ap_xml_parse_input()就可以将request的内容解析为apr_xml_doc格式,下面是一个实例程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <ctype.h>
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"
#include <apr_xml.h>
#define apr_isspace(c) (isspace(((unsigned char)(c))))
//if you using g++ to compile the module, extern should be critical.
extern module AP_MODULE_DECLARE_DATA soap_module;
extern "C" int ap_xml_parse_input (request_rec * r,apr_xml_doc ** pdoc);
const char * soap_xml_get_cdata(const apr_xml_elem *elem, apr_pool_t *pool,
int strip_white);
/* The sample content handler */
void dump_xml(request_rec *r, apr_xml_elem *root){
if(root==NULL)
return;
if(root->name!=NULL)
ap_rprintf(r," Name:%s \n", root->name);
if(root->lang!=NULL)
ap_rprintf(r," lang:%s \n", root->lang );
if(root->attr!=NULL)
ap_rprintf(r," Attr: %s:%s \n", root->attr->name,root->attr->value );
if(root->first_cdata.first!=NULL){
apr_text *itr=root->first_cdata.first;
for(;itr!=NULL;itr=itr->next)
ap_rprintf(r," cdata:%s \n", itr->text);
}
if(root->following_cdata.first!=NULL){
apr_text *itr=root->following_cdata.first;
for(;itr!=NULL;itr=itr->next)
ap_rprintf(r," following_cdata:%s \n", itr->text);
}
/*
const char * buf=soap_xml_get_cdata(root, r->pool,1);
if(buf==NULL)
ap_rprintf(r,"Buf is NULL");
else
ap_rprintf(r," Value: %s \n", buf );
*/
dump_xml(r,root->first_child);
dump_xml(r,root->next);
//dump_xml(r,root->parent);
}
static int mod_soap_handler(request_rec *r)
{
int result;
if (strcmp(r->handler, "mod_soap")) {
return DECLINED;
}
r->content_type = "text/html";
apr_xml_doc *doc = NULL;
if ((result = ap_xml_parse_input(r, &doc)) != 0)
{
ap_rputs("parse the xml fail\n", r);
}
if(doc==NULL)
{
ap_rprintf(r," Doc is NULL");
return OK;
}
if(doc->root==NULL)
{
ap_rprintf(r," root is NULL");
return OK;
}
dump_xml(r,doc->root);
return OK;
}
static void mod_soap_register_hooks(apr_pool_t *p)
{
ap_hook_handler(mod_soap_handler, NULL, NULL, APR_HOOK_MIDDLE);
}
/* Dispatch list for API hooks */
module AP_MODULE_DECLARE_DATA soap_module = {
STANDARD20_MODULE_STUFF,
NULL, /* create per-dir config structures */
NULL, /* merge per-dir config structures */
NULL, /* create per-server config structures */
NULL, /* merge per-server config structures */
NULL, /* table of config file commands */
mod_soap_register_hooks /* register hooks */
};

Install这个module,之后向apache发送一个SOAP request,例如:

1
2
3
4
5
6
7
8
9
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:AventailLogonV2">
<soapenv:Header/>
<soapenv:Body>
<urn:getMicroInterrogationList soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<version xsi:type="xsd:int">2</version>
<logonId xsi:type="xsd:string">QAABAFrlfQsUJvsQVh5kRozpavo=</logonId>
</urn:getMicroInterrogationList>
</soapenv:Body>
</soapenv:Envelope>

Apache server会回复如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
Name:Envelope
cdata:
Name:Header
cdata:
Name:Body
cdata:
Name:getMicroInterrogationList
cdata:
Name:version
cdata:2
Name:logonId
cdata:QAABAFrlfQsUJvsQVh5kRozpavo=

结语

进一步的开发就很容易了,只要按照自己的需要去处理数据就可以了。