用 sax 方式的时候,要自己构建3个函数,而且要直接用这三的函数来返回数据,要求较强的逻辑。在处理不同结构的 xml 的时候,还要重新进行构造这三个函数,麻烦!
用 dom 方式,倒是好些,但是他把每个节点都看作是一个 node,,操作起来要写好多的代码,麻烦!
网上有好多的开源的 xml 解析的类库,以前看过几个,但是心里总是觉得不踏实,感觉总是跟在别人的屁股后面。
这几天在搞 Java,挺累的,所以决定换换脑袋,写点 PHP 代码,为了防止以后 XML 解析过程再令我犯难,就花了一天的时间写了下面一个 XML 解析的类,于是就有了下面的东西。
实现方式是通过包装“sax方式的解析结果”来实现的。总的来说,对于我个人来说挺实用的,性能也还可以,基本上可以完成大多数的处理要求。
** 功能:
** 1\ 对基本的 XML 文件的节点进行 查询 / 添加 / 修改 / 删除 工作。
2\ 导出 XML 文件的所有数据到一个数组里面。
3\ 整个设计采用了 OO 方式,在操作结果集的时候,使用方法类似于 dom
** 缺点:
** 1\ 每个节点最好都带有一个id(看后面的例子),每个“节点名字”=“节点的标签_节点的id”,如果这个 id 值没有设置,程序将自动给他产生一个 id,这个 id 就是这个节点在他的上级节点中的位置编号,从 0 开始。
2\ 查询某个节点的时候可以通过用“|”符号连接“节点名字”来进行。这些“节点名字”都是按顺序写好的上级节点的名字。
** 使用说明:
** 运行下面的例子,在执行结果页面上可以看到函数的使用说明
代码是通过 PHP5 来实现的,在 PHP4 中无法正常运行。
由于刚刚写完,所以没有整理文档, ** 下面的例子演示 ** 的只是一部分的功能,代码不是很难,要是想知道更多的功能,可以研究研究源代码。
目录结构:
test.php
test.xml
xml / SimpleDocumentBase.php
xml / SimpleDocumentNode.php
xml / SimpleDocumentRoot.php
xml / SimpleDocumentParser.php
文件:test.xml
1<shop>
2<name>华联</name>
3<address>北京长安街-9999号</address>
4<desc>连锁超市</desc>
5<cat id="food">
6<goods id="food11">
7<name>food11</name>
8<price>12.90</price>
9</goods>
10<goods id="food12">
11<name>food12</name>
12<price>22.10</price>
13<desc creator="hahawen">好东西推荐</desc>
14</goods>
15</cat>
16<cat>
17<goods id="tel21">
18<name>tel21</name>
19<price>1290</price>
20</goods>
21</cat>
22<cat id="coat">
23<goods id="coat31">
24<name>coat31</name>
25<price>112</price>
26</goods>
27<goods id="coat32">
28<name>coat32</name>
29<price>45</price>
30</goods>
31</cat>
32<special id="hot">
33<goods>
34<name>hot41</name>
35<price>99</price>
36</goods>
37</special>
38</shop>
文件:test.php
1
2require_once "xml/SimpleDocumentParser.php";
3require_once "xml/SimpleDocumentBase.php";
4require_once "xml/SimpleDocumentRoot.php";
5require_once "xml/SimpleDocumentNode.php";
6
7$test = new SimpleDocumentParser();
8$test->parse("test.xml");
9$dom = $test->getSimpleDocument();
10
11echo "
<pre>";
echo "<hr/><font color="red">";
echo "下面是通过函数getSaveData()返回的整个xml数据的数组";
echo "</font><hr/>";
print_r($dom->getSaveData());
echo "<hr/><font color="red">";
echo "下面是通过setValue()函数,给给根节点添加信息,添加后显示出结果xml文件的内容";
echo "</font><hr/>";
$dom->setValue("telphone", "123456789");
echo htmlspecialchars($dom->getSaveXml());
echo "<hr/><font color="red">";
echo "下面是通过getNode()函数,返回某一个分类下的所有商品的信息";
echo "</font><hr/>";
$obj = $dom->getNode("cat_food");
$nodeList = $obj->getNode();
foreach($nodeList as $node){
$data = $node->getValue();
echo "<font color="red">商品名:".$data[name]."</font><br/>";
print_R($data);
print_R($node->getAttribute());
}
echo "<hr/><font color="red">";
echo "下面是通过findNodeByPath()函数,返回某一商品的信息";
echo "</font><hr/>";
$obj = $dom->findNodeByPath("cat_food|goods_food11");
if(!is_object($obj)){
echo "该商品不存在";
}else{
$data = $obj->getValue();
echo "<font color="red">商品名:".$data[name]."</font><br/>";
print_R($data);
print_R($obj->getAttribute());
}
echo "<hr/><font color="red">";
echo "下面是通过setValue()函数,给商品"food11"添加属性, 然后显示添加后的结果";
echo "</font><hr/>";
$obj = $dom->findNodeByPath("cat_food|goods_food11");
$obj->setValue("leaveword", array("value"=>"这个商品不错", "attrs"=>array("author"=>"hahawen", "date"=>date('Y-m-d'))));
echo htmlspecialchars($dom->getSaveXml());
echo "<hr/><font color="red">";
echo "下面是通过removeValue()/removeAttribute()函数,给商品"food11"改变和删除属性, 然后显示操作后的结果";
echo "</font><hr/>";
$obj = $dom->findNodeByPath("cat_food|goods_food12");
$obj->setValue("name", "new food12");
$obj->removeValue("desc");
echo htmlspecialchars($dom->getSaveXml());
echo "<hr/><font color="red">";
echo "下面是通过createNode()函数,添加商品, 然后显示添加后的结果";
echo "</font><hr/>";
$obj = $dom->findNodeByPath("cat_food");
$newObj = $obj->createNode("goods", array("id"=>"food13"));
$newObj->setValue("name", "food13");
$newObj->setValue("price", 100);
echo htmlspecialchars($dom->getSaveXml());
echo "<hr/><font color="red">";
echo "下面是通过removeNode()函数,删除商品, 然后显示删除后的结果";
echo "</font><hr/>";
$obj = $dom->findNodeByPath("cat_food");
$obj->removeNode("goods_food12");
echo htmlspecialchars($dom->getSaveXml());
1
2文件:SimpleDocumentParser.php
/**
*================================================
*
@author hahawen(大龄青年)
@since 2004-12-04
@copyright Copyright (c) 2004, NxCoder Group
================================================
/
/
- class SimpleDocumentParser
- use SAX parse xml file, and build SimpleDocumentObject
- all this pachage's is work for xml file, and method is action as DOM.
- @package SmartWeb.common.xml
- @version 1.0
*/
class SimpleDocumentParser
{
private $domRootObject = null;
private $currentNO = null;
private $currentName = null;
private $currentValue = null;
private $currentAttribute = null;
public
function getSimpleDocument()
{
return $this->domRootObject;
}
public function parse($file)
{
$xmlParser = xml_parser_create();
xml_parser_set_option($xmlParser,XML_OPTION_CASE_FOLDING,
0);
xml_parser_set_option($xmlParser,XML_OPTION_SKIP_WHITE, 1);
xml_parser_set_option($xmlParser,
XML_OPTION_TARGET_ENCODING, 'UTF-8');
xml_set_object($xmlParser, $this);
xml_set_element_handler($xmlParser, "startElement", "endElement");
xml_set_character_data_handler($xmlParser,
"characterData");
if (!xml_parse($xmlParser, file_get_contents($file)))
die(sprintf("XML error: %s at line %d", xml_error_string(xml_get_error_code($xmlParser)),
xml_get_current_line_number($xmlParser)));
xml_parser_free($xmlParser);
}
private function startElement($parser, $name, $attrs)
{
$this->currentName = $name;
$this->currentAttribute = $attrs;
if($this->currentNO == null)
{
$this->domRootObject = new SimpleDocumentRoot($name);
$this->currentNO = $this->domRootObject;
}
else
{
$this->currentNO = $this->currentNO->createNode($name, $attrs);
}
}
private function endElement($parser, $name)
{
if($this->currentName==$name)
{
$tag = $this->currentNO->getSeq();
$this->currentNO = $this->currentNO->getPNodeObject();
if($this->currentAttribute!=null && sizeof($this->currentAttribute)>0)
$this->currentNO->setValue($name, array('value'=>$this->currentValue, 'attrs'=>$this->currentAttribute));
else
$this->currentNO->setValue($name, $this->currentValue);
$this->currentNO->removeNode($tag);
}
else
{
$this->currentNO = (is_a($this->currentNO, 'SimpleDocumentRoot'))? null:
$this->currentNO->getPNodeObject();
}
}
private function characterData($parser, $data)
{
$this->currentValue = iconv('UTF-8', 'GB2312', $data);
}
function __destruct()
{
unset($this->domRootObject);
}
}
1
2文件:SimpleDocumentBase.php
/**
*=================================================
*
- @author hahawen(大龄青年)
- @since 2004-12-04
- @copyright Copyright (c) 2004, NxCoder Group
=================================================
/
/
- abstract class SimpleDocumentBase
- base class for xml file parse
- all this pachage's is work for xml file, and method is action as DOM.
- 1\ add/update/remove data of xml file.
- 2\ explode data to array.
- 3\ rebuild xml file
- @package SmartWeb.common.xml
- @abstract
- @version 1.0
*/
abstract class SimpleDocumentBase
{
private $nodeTag = null;
private $attributes = array();
private $values =
array();
private $nodes = array();
function __construct($nodeTag)
{
$this->nodeTag = $nodeTag;
}
public function getNodeTag()
{
return $this->nodeTag;
}
public function setValues($values){
$this->values = $values;
}
public function setValue($name, $value)
{
$this->values[$name] = $value;
}
public function getValue($name=null)
{
return $name==null?
$this->values: $this->values[$name];
}
public function removeValue($name)
{
unset($this->values["$name"]);
}
public function setAttributes($attributes){
$this->attributes = $attributes;
}
public function setAttribute($name, $value)
{
$this->attributes[$name] = $value;
}
public function getAttribute($name=null)
{
return $name==null? $this->attributes:
$this->attributes[$name];
}
public function removeAttribute($name)
{
unset($this->attributes["$name"]);
}
public function getNodesSize()
{
return sizeof($this->nodes);
}
protected function setNode($name, $nodeId)
{
$this->nodes[$name]
= $nodeId;
}
public abstract function createNode($name, $attributes);
public abstract function removeNode($name);
public abstract function getNode($name=null);
protected function getNodeId($name=null)
{
return $name==null? $this->nodes: $this->nodes[$name];
}
protected function createNodeByName($rootNodeObj, $name, $attributes, $pId)
{
$tmpObject = $rootNodeObj->createNodeObject($pId, $name, $attributes);
$key = isset($attributes[id])?
$name.''.$attributes[id]: $name.''.$this->getNodesSize();
$this->setNode($key, $tmpObject->getSeq());
return $tmpObject;
}
protected function removeNodeByName($rootNodeObj, $name)
{
$rootNodeObj->removeNodeById($this->getNodeId($name));
if(sizeof($this->nodes)==1)
$this->nodes = array();
else
unset($this->nodes[$name]);
}
protected function getNodeByName($rootNodeObj, $name=null)
{
if($name==null)
{
$tmpList = array();
$tmpIds = $this->getNodeId();
foreach($tmpIds as $key=>$id)
$tmpList[$key] = $rootNodeObj->getNodeById($id);
return $tmpList;
}
else
{
$id = $this->getNodeId($name);
if($id===null)
{
$tmpIds = $this->getNodeId();
foreach($tmpIds as $tkey=>$tid)
{
if(strpos($key, $name)==0)
{
$id = $tid;
break;
}
}
}
return $rootNodeObj->getNodeById($id);
}
}
public function findNodeByPath($path)
{
$pos = strpos($path, '|');
if($pos<=0)
{
return $this->getNode($path);
}
else
{
$tmpObj = $this->getNode(substr($path, 0,
$pos));
return is_object($tmpObj)?
$tmpObj->findNodeByPath(substr($path,
$pos+1)):
null;
}
}
public function getSaveData()
{
$data = $this->values;
if(sizeof($this->attributes)>0)
$data[attrs] = $this->attributes;
$nodeList = $this->getNode();
if($nodeList==null)
return $data;
foreach($nodeList as $key=>$node)
{
$data[$key] = $node->getSaveData();
}
return $data;
}
public function getSaveXml($level=0)
{
$prefixSpace
= str_pad("",
$level, "\t");
$str = "$prefixSpace<$this->nodeTag";
foreach($this->attributes as $key=>$value)
$str .= " $key="$value"";
$str .= ">\r\n";
foreach($this->values as $key=>$value){
if(is_array($value))
{
$str .= "$prefixSpace\t<$key";
foreach($value[attrs] as $attkey=>$attvalue)
$str .= " $attkey="$attvalue"";
$tmpStr = $value[value];
}
else
{
$str .= "$prefixSpace\t<$key";
$tmpStr = $value;
}
$tmpStr = trim(trim($tmpStr, "\r\n"));
$str .= ($tmpStr===null || $tmpStr==="")? " />\r\n": ">$tmpStr<!--$key-->\r\n";
}
foreach($this->getNode() as $node)
$str .= $node->getSaveXml($level+1)."\r\n";
$str .= "$prefixSpace<!--$this--->nodeTag>";
return $str;
}
function __destruct()
{
unset($this->nodes, $this->attributes, $this->values);
}
}
1
2
3文件:SimpleDocumentRoot.php
/**
*==============================================
*
- @author hahawen(大龄青年)
- @since 2004-12-04
- @copyright Copyright (c) 2004, NxCoder Group
==============================================
/
/
- class SimpleDocumentRoot
- xml root class, include values/attributes/subnodes.
- all this pachage's is work for xml file, and method is action as DOM.
- @package SmartWeb.common.xml
- @version 1.0
*/
class SimpleDocumentRoot extends SimpleDocumentBase
{
private $prefixStr = '';
private $nodeLists = array();
function __construct($nodeTag)
{
parent::__construct($nodeTag);
}
public function createNodeObject($pNodeId, $name, $attributes)
{
$seq = sizeof($this->nodeLists);
$tmpObject = new SimpleDocumentNode($this,
$pNodeId, $name, $seq);
$tmpObject->setAttributes($attributes);
$this->nodeLists[$seq] = $tmpObject;
return $tmpObject;
}
public function removeNodeById($id)
{
if(sizeof($this->nodeLists)==1)
$this->nodeLists = array();
else
unset($this->nodeLists[$id]);
}
public function getNodeById($id)
{
return $this->nodeLists[$id];
}
public function createNode($name, $attributes)
{
return $this->createNodeByName($this, $name, $attributes, -1);
}
public function removeNode($name)
{
return $this->removeNodeByName($this, $name);
}
public function getNode($name=null)
{
return $this->getNodeByName($this, $name);
}
public function getSaveXml()
{
$prefixSpace = "";
$str = $this->prefixStr."\r\n";
return $str.parent::getSaveXml(0);
}
}
1
2文件:SimpleDocumentNode.php
/**
*===============================================
*
- @author hahawen(大龄青年)
- @since 2004-12-04
- @copyright Copyright (c) 2004, NxCoder Group
===============================================
/
/
- class SimpleDocumentNode
- xml Node class, include values/attributes/subnodes.
- all this pachage's is work for xml file, and method is action as DOM.
- @package SmartWeb.common.xml
- @version 1.0
*/
class SimpleDocumentNode extends SimpleDocumentBase
{
private $seq = null;
private $rootObject = null;
private $pNodeId = null;
function __construct($rootObject, $pNodeId, $nodeTag, $seq)
{
parent::__construct($nodeTag);
$this->rootObject = $rootObject;
$this->pNodeId = $pNodeId;
$this->seq = $seq;
}
public function getPNodeObject()
{
return ($this->pNodeId==-1)?
$this->rootObject:
$this->rootObject->getNodeById($this->pNodeId);
}
public function getSeq(){
return $this->seq;
}
public function createNode($name, $attributes)
{
return $this->createNodeByName($this->rootObject,
$name, $attributes,
$this->getSeq());
}
public function removeNode($name)
{
return $this->removeNodeByName($this->rootObject, $name);
}
public function getNode($name=null)
{
return $this->getNodeByName($this->rootObject,
$name);
}
}
1
2
3下面是例子运行对结果
4
5* * *
6
7下面是通过函数getSaveData()返回的整个xml数据的数组
8
9* * *
10
11Array
12(
13[name] => 华联
14[address] => 北京长安街-9999号
15[desc] => 连锁超市
16[cat_food] => Array
17(
18[attrs] => Array
19(
20[id] => food
21)
22
23[goods_food11] => Array
24(
25[name] => food11
26[price] => 12.90
27[attrs] => Array
28(
29[id] => food11
30)
31
32)
33
34[goods_food12] => Array
35(
36[name] => food12
37[price] => 22.10
38[desc] => Array
39(
40[value] => 好东西推荐
41[attrs] => Array
42(
43[creator] => hahawen
44)
45
46)
47
48[attrs] => Array
49(
50[id] => food12
51)
52
53)
54
55)
56
57[cat_1] => Array
58(
59[goods_tel21] => Array
60(
61[name] => tel21
62[price] => 1290
63[attrs] => Array
64(
65[id] => tel21
66)
67
68)
69
70)
71
72[cat_coat] => Array
73(
74[attrs] => Array
75(
76[id] => coat
77)
78
79[goods_coat31] => Array
80(
81[name] => coat31
82[price] => 112
83[attrs] => Array
84(
85[id] => coat31
86)
87
88)
89
90[goods_coat32] => Array
91(
92[name] => coat32
93[price] => 45
94[attrs] => Array
95(
96[id] => coat32
97)
98
99)
100
101)
102
103[special_hot] => Array
104(
105[attrs] => Array
106(
107[id] => hot
108)
109
110[goods_0] => Array
111(
112[name] => hot41
113[price] => 99
114)
115
116)
117
118)
119
120
121* * *
122
123下面是通过setValue()函数,给给根节点添加信息,添加后显示出结果xml文件的内容
124
125* * *
126
127<?xml version="1.0" encoding="GB2312" ?>
128<shop>
129<name>华联</name>
130<address>北京长安街-9999号</address>
131<desc>连锁超市</desc>
132<telphone>123456789</telphone>
133<cat id="food">
134<goods id="food11">
135<name>food11</name>
136<price>12.90</price>
137</goods>
138<goods id="food12">
139<name>food12</name>
140<price>22.10</price>
141<desc creator="hahawen">好东西推荐</desc>
142</goods>
143</cat>
144<cat>
145<goods id="tel21">
146<name>tel21</name>
147<price>1290</price>
148</goods>
149</cat>
150<cat id="coat">
151<goods id="coat31">
152<name>coat31</name>
153<price>112</price>
154</goods>
155<goods id="coat32">
156<name>coat32</name>
157<price>45</price>
158</goods>
159</cat>
160<special id="hot">
161<goods>
162<name>hot41</name>
163<price>99</price>
164</goods>
165</special>
166</shop>
167
168* * *
169
170下面是通过getNode()函数,返回某一个分类下的所有商品的信息
171
172* * *
173
174商品名:food11
175Array
176(
177[name] => food11
178[price] => 12.90
179)
180Array
181(
182[id] => food11
183)
184商品名:food12
185Array
186(
187[name] => food12
188[price] => 22.10
189[desc] => Array
190(
191[value] => 好东西推荐
192[attrs] => Array
193(
194[creator] => hahawen
195)
196
197)
198
199)
200Array
201(
202[id] => food12
203)
204
205
206* * *
207
208下面是通过findNodeByPath()函数,返回某一商品的信息
209
210* * *
211
212商品名:food11
213Array
214(
215[name] => food11
216[price] => 12.90
217)
218Array
219(
220[id] => food11
221)
222
223
224* * *
225
226下面是通过setValue()函数,给商品"food11"添加属性, 然后显示添加后的结果
227
228* * *
229
230<?xml version="1.0" encoding="GB2312" ?>
231<shop>
232<name>华联</name>
233<address>北京长安街-9999号</address>
234<desc>连锁超市</desc>
235<telphone>123456789</telphone>
236<cat id="food">
237<goods id="food11">
238<name>food11</name>
239<price>12.90</price>
240<leaveword author="hahawen" date="2004-12-05">这个商品不错</leaveword>
241</goods>
242<goods id="food12">
243<name>food12</name>
244<price>22.10</price>
245<desc creator="hahawen">好东西推荐</desc>
246</goods>
247</cat>
248<cat>
249<goods id="tel21">
250<name>tel21</name>
251<price>1290</price>
252</goods>
253</cat>
254<cat id="coat">
255<goods id="coat31">
256<name>coat31</name>
257<price>112</price>
258</goods>
259<goods id="coat32">
260<name>coat32</name>
261<price>45</price>
262</goods>
263</cat>
264<special id="hot">
265<goods>
266<name>hot41</name>
267<price>99</price>
268</goods>
269</special>
270</shop>
271
272* * *
273
274下面是通过removeValue()/removeAttribute()函数,给商品"food11"改变和删除属性, 然后显示操作后的结果
275
276* * *
277
278<?xml version="1.0" encoding="GB2312" ?>
279<shop>
280<name>华联</name>
281<address>北京长安街-9999号</address>
282<desc>连锁超市</desc>
283<telphone>123456789</telphone>
284<cat id="food">
285<goods id="food11">
286<name>food11</name>
287<price>12.90</price>
288<leaveword author="hahawen" date="2004-12-05">这个商品不错</leaveword>
289</goods>
290<goods id="food12">
291<name>new food12</name>
292<price>22.10</price>
293</goods>
294</cat>
295<cat>
296<goods id="tel21">
297<name>tel21</name>
298<price>1290</price>
299</goods>
300</cat>
301<cat id="coat">
302<goods id="coat31">
303<name>coat31</name>
304<price>112</price>
305</goods>
306<goods id="coat32">
307<name>coat32</name>
308<price>45</price>
309</goods>
310</cat>
311<special id="hot">
312<goods>
313<name>hot41</name>
314<price>99</price>
315</goods>
316</special>
317</shop>
318
319* * *
320
321下面是通过createNode()函数,添加商品, 然后显示添加后的结果
322
323* * *
324
325<?xml version="1.0" encoding="GB2312" ?>
326<shop>
327<name>华联</name>
328<address>北京长安街-9999号</address>
329<desc>连锁超市</desc>
330<telphone>123456789</telphone>
331<cat id="food">
332<goods id="food11">
333<name>food11</name>
334<price>12.90</price>
335<leaveword author="hahawen" date="2004-12-05">这个商品不错</leaveword>
336</goods>
337<goods id="food12">
338<name>new food12</name>
339<price>22.10</price>
340</goods>
341<goods id="food13">
342<name>food13</name>
343<price>100</price>
344</goods>
345</cat>
346<cat>
347<goods id="tel21">
348<name>tel21</name>
349<price>1290</price>
350</goods>
351</cat>
352<cat id="coat">
353<goods id="coat31">
354<name>coat31</name>
355<price>112</price>
356</goods>
357<goods id="coat32">
358<name>coat32</name>
359<price>45</price>
360</goods>
361</cat>
362<special id="hot">
363<goods>
364<name>hot41</name>
365<price>99</price>
366</goods>
367</special>
368</shop>
369
370* * *
371
372下面是通过removeNode()函数,删除商品, 然后显示删除后的结果
373
374* * *
375
376<?xml version="1.0" encoding="GB2312" ?>
377<shop>
378<name>华联</name>
379<address>北京长安街-9999号</address>
380<desc>连锁超市</desc>
381<telphone>123456789</telphone>
382<cat id="food">
383<goods id="food11">
384<name>food11</name>
385<price>12.90</price>
386<leaveword author="hahawen" date="2004-12-05">这个商品不错</leaveword>
387</goods>
388<goods id="food13">
389<name>food13</name>
390<price>100</price>
391</goods>
392</cat>
393<cat>
394<goods id="tel21">
395<name>tel21</name>
396<price>1290</price>
397</goods>
398</cat>
399<cat id="coat">
400<goods id="coat31">
401<name>coat31</name>
402<price>112</price>
403</goods>
404<goods id="coat32">
405<name>coat32</name>
406<price>45</price>
407</goods>
408</cat>
409<special id="hot">
410<goods>
411<name>hot41</name>
412<price>99</price>
413</goods>
414</special>
415</shop></pre>