解析xml文档的类库有很多种,比较常用的就是SimpleXML,DOM Document,XMLReader,基于SAX的xml_parse函数。
根据xml文档结构,以及需求的不同,要选择好对应的类库。
解析小的文档的话,使用SimpleXML比较合适,简单的几个函数就可以搞定。
如果文档非常巨大,最好是XMLReader打开,中间转换成DOM来解析,感觉DOM与XPath结合的比较好。
以上是解析已知文档结构的XML,比如知道使用了什么命名空间,有哪些主要的节点等等信息的XML文档。
如果是要解析任意的XML文档,还是SAX的xml_parse比较合适,自己定义三个handler函数。
比如笔者就是用xml_parse来解析任意的xml文档,生成json格式的字符串。
下面把使用xml_parse来解析xml文档的所需要的三个handler讲解一遍。
首先是三个变量
$depth是指xml文档结构的层数,第一层的值是0。
$json_array是一个多位数组,每一个元素的成员是一个json数组,直接可以用json_encode函数。
这个数组的第一维的index是上面的$depth,解析完每一层的xml结构后,都将其结果"粘贴"到其父节点的最后一个字节点后边。
解析完成后$json_array[0]就是所有的xml文档内容都在里面啦。
$tag_name_stack也是一个数组,后进先出的数组,正在处理的标签名就存在这里面,处理完一个标签后,就将其请出数组。
首先是把该标签的属性都装入一个数组
foreach ($attrs as $attr_name => $attr_value) {
$attr_name = str_replace(":", "$", $attr_name);
$arrts_array[$attr_name]=$attr_value;
}
$tag_name = str_replace(":", "$", $name);
$this->tag_name_stack[] = $tag_name; // 标签名入栈
$this->depth++; // 层数递增
// 将属性值放入json_array中
$this->json_array[$this->depth][$tag_name] = $arrts_array;
}
下面的cdata_handler就是处理标签的text文本或者cdata文本
// 在json_array中,文本信息用$t来作为index
// 这个isset的判断是因为, cdata_handler不像start_handler和end_handler只是进入一次,
// 要注意哦,cdata_handler函数,是有可能进入多次的哦,所以要用isset判断,以及.=操作符。
// 关于cdata_handler的多次进入,请参考:
http://jp2.php.net/manual/en/function.xml-set-character-data-handler.php
"ken at positive-edge dot com " 这个家伙的回复。
if (!isset($this->json_array[$this->depth][$tag_name]["/$t"])) {
//$this->json_array[$this->depth][$tag_name] = array("/$t" => $data);
$this->json_array[$this->depth][$tag_name]["/$t"] = $data;
} else {
$this->json_array[$this->depth][$tag_name]["/$t"] .= $data;
}
最后就是end_handler,这里最重要,处理完的标签,要找准自己的父节点,
要跟在父节点的最后一个子节点后面,不要不小心把自己的兄弟姐妹都给覆盖了哦。
而且,如果有跟自己同名的兄弟姐妹的话,也要能够正确处理!
private function end_handler($parser, $name) {
// 因为已经调用过了start_handler和cdata_handler,所以自己后面也会拖家带口的,属性啦,文本啦,子节点啦
// 全都把他们先提取出来
$current_tag_name = $this->tag_name_stack[$this->depth];
$current_element = $this->json_array[$this->depth][$current_tag_name];
if ($this->depth > 0) { // depth是0的话,这就是根节点啦,不用找父节点和处理兄弟姐妹关系了
// 找出父节点的标签名
$parent_tag_name = $this->tag_name_stack[$this->depth-1];
// 父节点,那就是上一层咯,这里就是判断自己有没有同名的兄弟姐妹了。
if (!array_key_exists($current_tag_name, $this->json_array[$this->depth-1][$parent_tag_name])) {
// 没有同名兄弟姐妹?那敢情好,直接挂到父节点上就ok了,
// 要注意最后一个[]哦,自己的标签名作为index,不要忘记了这是json数组
$this->json_array[$this->depth-1][$parent_tag_name][$current_tag_name] = $current_element;
} else {
// 我不寂寞,我是有同名兄弟姐妹的,这里的$child就是我的同名兄弟姐妹了。
$child = $this->json_array[$this->depth-1][$parent_tag_name][$current_tag_name];
if (isset($child[0])) {
// 如果$child数组有0作为index的元素,说明我不只是一个同名兄弟姐妹哦
$this->json_array[$this->depth-1][$parent_tag_name][$current_tag_name][] = $current_element;
} else {
// 前面只有一个同名兄弟姐妹,我们要组成一个数组,然后才给$current_tag_name
// 下次如果还有另外一名同名兄弟姐妹,那么它就会进入前面的分支
unset($this->json_array[$this->depth-1][$parent_tag_name][$current_tag_name]);
$this->json_array[$this->depth-1][$parent_tag_name][$current_tag_name][] = $child;
$this->json_array[$this->depth-1][$parent_tag_name][$current_tag_name][] = $current_element;
}
}
array_pop($this->tag_name_stack); // 最后一个,也就是正在处理的标签名出栈
// unset这一层的数组。因为都粘到父节点上去了,自己这一层的数组没必要保留,
// 不过保留也可以,但是在处理下一个同层的兄弟姐妹时,要记得把我覆盖掉哦!
unset($this->json_array[$this->depth]);
$this->depth--; // 层数递减,返回父节点的end_handler,或者到同层兄弟节点的start_handler去。
}
}
上面的解说可能不适合初学者,比如xml_parse以及,json格式数组等等知识
不过都可以在www.php.net上找到资料
xml_parse相关: http://jp2.php.net/manual/en/function.xml-parse.php
json_encode相关: http://jp2.php.net/manual/en/function.json-encode.php