PHP操作XML方法之 XML Expat Parser
XML Expat Parser 简介
此PHP扩展实现了使用PHP支持JamesClark编写的expat。此工具包可解析(但不能验证)XML文档。它支持PHP所提供的3种字符编码:US-ASCII, ISO-8859-1和UTF-8。不支持 UTF-16。
此扩展可创建XML解析器并为不同的XML事件定义处理程序(handler)。每个XML解析器还存在少数可以调节的参数。
提供的函数
- utf8_decode — 将用 UTF-8 方式编码的 ISO-8859-1 字符串转换成单字节的 ISO-8859-1 字符串。
- utf8_encode — 将 ISO-8859-1 编码的字符串转换为 UTF-8 编码
- xml_error_string — 获取 XML 解析器的错误字符串
- xml_get_current_byte_index — 获取 XML 解析器的当前字节索引
- xml_get_current_column_number — 获取 XML 解析器的当前列号
- xml_get_current_line_number — 获取 XML 解析器的当前行号
- xml_get_error_code — 获取 XML 解析器错误代码
- xml_parse_into_struct — 将 XML 数据解析到数组中
- xml_parse — 开始解析一个 XML 文档
- xml_parser_create_ns — 生成一个支持命名空间的 XML 解析器
- xml_parser_create — 建立一个 XML 解析器
- xml_parser_free — 释放指定的 XML 解析器
- xml_parser_get_option — 从 XML 解析器获取选项设置信息
- xml_parser_set_option — 为指定 XML 解析进行选项设置
- xml_set_character_data_handler — 建立字符数据处理器
- xml_set_default_handler — 建立默认处理器
- xml_set_element_handler — 建立起始和终止元素处理器
- xml_set_end_namespace_decl_handler — 建立终止命名空间声明处理器
- xml_set_external_entity_ref_handler — 建立外部实体指向处理器
- xml_set_notation_decl_handler — 建立注释声明处理器
- xml_set_object — 在对象中使用 XML 解析器
- xml_set_processing_instruction_handler — 建立处理指令(PI)处理器
- xml_set_start_namespace_decl_handler — 建立起始命名空间声明处理器
- xml_set_unparsed_entity_decl_handler — 建立未解析实体定义声明处理器
更多请参考官网:http://php.net/manual/zh/book.xml.php
PS:其中粗体函数表示下文会使用到
使用主要步骤
- 创建解析器:xml_parser_create()
- 处理标签及数据:xml_parse_into_struct() 或 xml_set_element_handler() 和 xml_set_character_data_handler()
- 释放资源:xml_parser_free() 释放资源
使用xml_parse_into_struct函数把XML导入数组中
读取XML并导入到数组中,打印看结果
$xml = <<<XML
<document><item><id>1</id><name>luyuqiang</name><age>90s</age></item><item><id>2</id><name>lixiaolai</name></item><item><id>3</id><name>ruanyifeng</name><age>70s</age></item></document>
XML;
$p = xml_parser_create();
xml_parse_into_struct($p, $xml, $vals, $index);
xml_parser_free($p);
echo "Index array\n";
print_r($index);
echo "\nVals array\n";
print_r($vals);
输出结果如下:
Index array
Array
(
[DOCUMENT] => Array
(
[0] => 0
[1] => 12
)
[ITEM] => Array
(
[0] => 1
[1] => 4
[2] => 5
[3] => 7
[4] => 8
[5] => 11
)
[NAME] => Array
(
[0] => 2
[1] => 6
[2] => 9
)
[AGE] => Array
(
[0] => 3
[1] => 10
)
)
Vals array
Array
(
[0] => Array
(
[tag] => DOCUMENT
[type] => open
[level] => 1
)
[1] => Array
(
[tag] => ITEM
[type] => open
[level] => 2
)
[2] => Array
(
[tag] => NAME
[type] => complete
[level] => 3
[value] => luyuqiang
)
[3] => Array
(
[tag] => AGE
[type] => complete
[level] => 3
[value] => 90s
)
[4] => Array
(
[tag] => ITEM
[type] => close
[level] => 2
)
[5] => Array
(
[tag] => ITEM
[type] => open
[level] => 2
)
[6] => Array
(
[tag] => NAME
[type] => complete
[level] => 3
[value] => lixiaolai
)
[7] => Array
(
[tag] => ITEM
[type] => close
[level] => 2
)
[8] => Array
(
[tag] => ITEM
[type] => open
[level] => 2
)
[9] => Array
(
[tag] => NAME
[type] => complete
[level] => 3
[value] => ruanyifeng
)
[10] => Array
(
[tag] => AGE
[type] => complete
[level] => 3
[value] => 70s
)
[11] => Array
(
[tag] => ITEM
[type] => close
[level] => 2
)
[12] => Array
(
[tag] => DOCUMENT
[type] => close
[level] => 1
)
)
官网是这么解释xml_parse_into_struct函数的:
该函数将XML文件解析到两个对应的数组中,index参数含有指向values数组中对应值的指针。最后两个数组参数可由指针传递给函数。
结合输出很好理解'index参数含有指向values数组中对应值的指针'这句话了。
其中值指针变量的第二维数组中包含tag,type,level,value等索引,tag是标签名,type是类型,level是维度,value是标签值,type包括:open(起始标签),close(闭合标签),complete(完整标签)
PS:所有标签都是大写,是因为大写转换
遍历获取数据
$peopleNum = count(($index['ITEM']))/2;
for($i=0;$i<$peopleNum;$i++){
$person[$i]['name'] = !empty($vals[$index['NAME'][$i]]['value']) ? $vals[$index['NAME'][$i]]['value'] : '';
$person[$i]['age'] = !empty($vals[$index['AGE'][$i]]['value']) ? $vals[$index['AGE'][$i]]['value'] : 'unknown';
}
print_r($person);
//结果如下:
Array
(
[0] => Array
(
[name] => luyuqiang
[age] => 90s
)
[1] => Array
(
[name] => lixiaolai
[age] => 70s
)
[2] => Array
(
[name] => ruanyifeng
[age] => unknown
)
)
从结果可以看出:XML中的数据明明是lixiaolai的年龄未知,但是直接用for遍历,就会出现‘错位’的情况,结果ruanyifeng的年龄是未知。
因此如果不能保证XML中每组数据都是完整的,写代码时就要小心!可以用下列代码得到正确结果:
$itemNum = count($index['ITEM');
for($i = 0;$i < $itemNum;$i += 2){
$startItem = $index['ITEM'][$i];
$closeItem = $index['ITEM'][$i+1];
$idx = $startItem + 1;
$num = ($closeItem - 1) - $startItem;
$tempArr = array_slice($vals,$idx,$num);
//标签默认值
$person[$i] = array('NAME'=>null,'AGE'=>'unknown');
foreach($tempArr as $one){
!empty($one['value']) && $person[$i][$one['tag']] = $one['value'];
}
}
print_r($person);
使用事件处理器读取XML
官网提供了7种事件处理函数,一些函数不易于理解且示例又少,所以本文只对 xml_set_element_handler() ,xml_set_character_data_handler() 和 xml_set_default_handler() 主要的3个函数做了实践:
//收集数据变量
$currentData = null;
$data = array();
$index = -1;
//创建XML解析器
$parser = xml_parser_create();
//创建带有命名空间的解析器,用':'分隔
//$parser = xml_parser_create_ns('utf-8',':');
//获取大写转换 默认:1 大写
//$xmloption = xml_parser_get_option ($parser,XML_OPTION_CASE_FOLDING);
//设置不大写转换
//xml_parser_set_option($parser,XML_OPTION_CASE_FOLDING,0);
//此时再获取为'0'
//$xmloption = xml_parser_get_option ($parser,XML_OPTION_CASE_FOLDING);
//获取目标编码 默认:utf-8 可设置 ISO-8859-1、US-ASCII
//$xmloption = xml_parser_get_option ($parser,XML_OPTION_TARGET_ENCODING);
//设置为'US-ASCII'
//xml_parser_set_option($parser,XML_OPTION_TARGET_ENCODING,'US-ASCII');
//此时再获取为'US-ASCII'
//$xmloption = xml_parser_get_option ($parser,XML_OPTION_TARGET_ENCODING);
//是否略过由白空字符组成的值
//xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
//xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0);
//指明在一个标记名前应略过几个字符。
//xml_parser_set_option($parser, XML_OPTION_SKIP_TAGSTART, 3);
//libXML下不支持此事件,因此不会调用注册的处理程序。
//xml_set_start_namespace_decl_handler($parser,function ($fp,$prefix,$uri){
// //命名空间开始
// echo $prefix.$uri.PHP_EOL;
//});
//xml_set_end_namespace_decl_handler($parser,function($fp,$prefix,$uri){
// //命名空间结束
// echo $prefix.$uri.PHP_EOL;
//});
xml_set_element_handler($parser,function($fp,$element,$attributes){
//标签开始回调函数
global $data,$index;
switch($element){
case 'ITEM' : $index++;$data[$index] = array('ID'=>null,'NAME'=>null,'AGE'=>'known',);break;
}
//处理标签属性
if($attributes){
foreach($attributes as $attrKey => $attrVal){
// echo 'attributes: '.$attrKey.'='.$attrVal.PHP_EOL;
switch($element){
case 'ITEM' :
$data[$index][$attrKey] = $attrVal;
break;
}
}
}
},function ($fp,$element) {
//标签结束回调函数
global $data, $index, $currentData;
if(in_array($element,array('NAME','AGE'))){
$data[$index][$element] = $currentData;
}
});
xml_set_character_data_handler($parser,function ($fp,$data){
//处理数据回调函数
global $index, $currentData;
$currentData = $data;
});
//没有定义开始,结束,处理数据的回调,默认回调函数,返回标签和值
//xml_set_default_handler($parser,function ($fp,$data){
//默认函数
//});
$file = 'baseXML.xml';
$fp = fopen($file,'r') or die('no file!');
while ($xmlData = fread($fp,4096)){
xml_parse($parser, $xmlData, feof($fp));
}
//释放解析器
xml_parser_free($parser);
print_r($data);
baseXML文件如下:
<document><item id="1"><name>luyuqiang</name><age>90s</age></item><item id="2"><name>lixiaolai</name></item><item id="3"><name>ruanyifeng</name><age>70s</age></item></document>
结果跟上边用xml_set_character_data_handler函数获取的是一样,这种方式显然更可控些。
类中使用XML解析器
把创建、销毁解析器、事件函数放到类的构造方法中,把事件回调函数作为类方法的实现,其实很简单:只需要使用xml_set_object函数即可。
官网示例已经很清楚了,这里就不做赘述了
结论
通过学习发现:解析器这种方式处理XML的兼容老版本的PHP(php4),其优点在于不是一次把XML加载到内存,其可以动态读取XML文件。缺点也很明显,不易于理解和开发。
PHP操作XML方法之 XML Expat Parser的更多相关文章
- C#操作XML方法集合
一 前言 先来了解下操作XML所涉及到的几个类及之间的关系 如果大家发现少写了一些常用的方法,麻烦在评论中指出,我一定会补上的!谢谢大家 * 1 XMLElement 主要是针对节点的一些属性进行操 ...
- Expat Parser解析xml文件
Expat 解析器是基于事件的解析器. 基于事件的解析器集中在 XML 文档的内容,而不是它们的结构.正因为如此,基于事件的解析器能够比基于树的解析器更快地访问数据. 请看下面的 XML 片段: &l ...
- C#操作XML方法详解
using System.Xml; //初始化一个xml实例 XmlDocument xml=new XmlDocument(); //导入指定xml文件 xml.Load(path); xml. ...
- 【转】C#对XML文件的各种操作实现方法
[转]C#对XML文件的各种操作实现方法 原文:http://www.jb51.net/article/35568.htm XML:Extensible Markup Language(可扩展标记语言 ...
- C#操作XML方法:新增、修改和删除节点与属性
一 前言 先来了解下操作XML所涉及到的几个类及之间的关系 如果大家发现少写了一些常用的方法,麻烦在评论中指出,我一定会补上的!谢谢大家 * 1 XMLElement 主要是针对节点的一些属性进行操 ...
- iOS 详解NSXMLParser方法解析XML数据方法
前一篇文章已经介绍了如何通过URL从网络上获取xml数据.下面介绍如何将获取到的数据进行解析. 下面先看看xml的数据格式吧! <?xml version="1.0" enc ...
- [转]Python存取XML方法简介
转自:http://www.cnblogs.com/salomon/archive/2012/05/28/2518648.html 目前而言,Python 3.2存取XML有以下四种方法: 1.Exp ...
- python 存取xml方法
或者也可以参考http://www.cnblogs.com/xiaowuyi/archive/2012/10/17/2727912.html中内容 目前而言,Python 3.2存取XML有以下四种方 ...
- 使用Spring Mvc 转发 带着模板 父页面 之解决方法 decorators.xml
周末了,周一布置的任务还没完毕,卡在了页面跳转上,接手了一个半截的项目要进行开发,之前没有人给培训.全靠自己爬代码,所以进度比較慢.并且加上之前没实用过 Spring Mvc 开发项目.所以有点吃力, ...
随机推荐
- Java面试宝典(4)Java基础部分
71.说出一些常用的类,包,接口,请各举5个 要让人家感觉你对java ee开发很熟,所以,不能仅仅只列core java中的那些东西,要多列你在做ssh项目中涉及的那些东西.就写你最近写的那些程序中 ...
- WPFのStyle TargetType的不同写法
一.隐式写法 <Style TargetType="TextBlock"> <Setter Property="/> </Style> ...
- elasticsearch 父子关系
ElasticSearch 中的Parent-Child关系和nested模型是相似的, 两个都可以用于复杂的数据结构中,区别是 nested 类型的文档是把所有的实体聚合到一个文档中而Parent- ...
- win10下安装Ubuntu后,启动时没有win10选项解决方法
通过在ubuntu里修改启动引导,解决. 1.进入Ubuntu系统,Ctrl+alt+t进入终端,输入以下命令即可 sudo gedit /etc/default/grub 2.在打开的gedit编辑 ...
- MySQL系统架构
已经很久没有写博客了,平时总感觉没有时间写,但是却有大把的时间去走神,去做一些无用的事情.写博客是挺锻炼人的一个过程,不仅锻炼写作能力,坚持写还能够大幅提升技术水平,写出来还能够有广大的网友帮助我们纠 ...
- 练手项目之image caption问题记录
小白一个,刚刚费了老大的劲完成一个练手项目--image caption,虽然跑通了,但是评估结果却惨不忍睹.于是贴上大神的作品,留待日后慢慢消化.顺便记录下自己踩坑的一些问题. 先膜拜下大神的作品. ...
- MYSQL中判断函数有哪些
新建一张客户表,如下:sex:1-男,2-女,3-未知:level是客户的级别:1-超级VIP客户,2-VIP客户,3-普通客户 方式一:case函数:流程控制函数 用法一: CASE express ...
- 使用Ueditor点击上传图片时显示延迟的问题
最近在做一个项目,需要用到Ueditor,但是在点击上传图片的时候,总是隔了4-5秒才显示文件框 查了一些资料,最后发现,只需在 修改:(1) dialog/images/image.js 把imag ...
- 【Dart学习】--Dart之字符串(String)的相关方法总结
字符串定义使用单引号或双引号 String a = "abcdefg"; String b = '; 创建多行字符串,保留内在格式使用三个单引号或三个双引号 创建多行字符串,保留内 ...
- Java中如何修改Jar中的内容
一.摘要 好长时间没写blog了,之前换了一家公司.表示工作更有战斗力了,可惜就是没时间写文章了.在这段时间其实是遇到很多问题的,只是都是记录下来,并没有花时间去研究解决.但是这周遇到这个问题没办法让 ...