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:其中粗体函数表示下文会使用到

使用主要步骤

  1. 创建解析器:xml_parser_create()
  2. 处理标签及数据:xml_parse_into_struct() 或 xml_set_element_handler() 和 xml_set_character_data_handler()
  3. 释放资源: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的更多相关文章

  1. C#操作XML方法集合

    一 前言 先来了解下操作XML所涉及到的几个类及之间的关系  如果大家发现少写了一些常用的方法,麻烦在评论中指出,我一定会补上的!谢谢大家 * 1 XMLElement 主要是针对节点的一些属性进行操 ...

  2. Expat Parser解析xml文件

    Expat 解析器是基于事件的解析器. 基于事件的解析器集中在 XML 文档的内容,而不是它们的结构.正因为如此,基于事件的解析器能够比基于树的解析器更快地访问数据. 请看下面的 XML 片段: &l ...

  3. C#操作XML方法详解

    using System.Xml; //初始化一个xml实例 XmlDocument xml=new XmlDocument();   //导入指定xml文件 xml.Load(path); xml. ...

  4. 【转】C#对XML文件的各种操作实现方法

    [转]C#对XML文件的各种操作实现方法 原文:http://www.jb51.net/article/35568.htm XML:Extensible Markup Language(可扩展标记语言 ...

  5. C#操作XML方法:新增、修改和删除节点与属性

    一 前言 先来了解下操作XML所涉及到的几个类及之间的关系  如果大家发现少写了一些常用的方法,麻烦在评论中指出,我一定会补上的!谢谢大家 * 1 XMLElement 主要是针对节点的一些属性进行操 ...

  6. iOS 详解NSXMLParser方法解析XML数据方法

    前一篇文章已经介绍了如何通过URL从网络上获取xml数据.下面介绍如何将获取到的数据进行解析. 下面先看看xml的数据格式吧! <?xml version="1.0" enc ...

  7. [转]Python存取XML方法简介

    转自:http://www.cnblogs.com/salomon/archive/2012/05/28/2518648.html 目前而言,Python 3.2存取XML有以下四种方法: 1.Exp ...

  8. python 存取xml方法

    或者也可以参考http://www.cnblogs.com/xiaowuyi/archive/2012/10/17/2727912.html中内容 目前而言,Python 3.2存取XML有以下四种方 ...

  9. 使用Spring Mvc 转发 带着模板 父页面 之解决方法 decorators.xml

    周末了,周一布置的任务还没完毕,卡在了页面跳转上,接手了一个半截的项目要进行开发,之前没有人给培训.全靠自己爬代码,所以进度比較慢.并且加上之前没实用过 Spring Mvc 开发项目.所以有点吃力, ...

随机推荐

  1. poj1426 Find The Multiple (DFS)

    题目: Find The Multiple Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 41845   Accepted: ...

  2. 安装软件 学习linux命令

    nm -D /usr/lib64/libstdc++.so.6 | grep GLIBCnm dumps named symbols, -D for dynamic libs, and grep fo ...

  3. C# 编程--数组

    数组 可以帮我我们一次声明存储多个相同类型的变量.用来解决同一类大量数据在内存存储和运算的功能特点:连续.同一类数据数组定义==>赋值==>取值    定义:        int[] n ...

  4. 2018-8-10-win10-UWP-用Path画图

    title author date CreateTime categories win10 UWP 用Path画图 lindexi 2018-08-10 19:16:50 +0800 2018-2-1 ...

  5. /var/lib/dpkg/lock

    记性不好: Could not get lock /var/lib/dpkg/lock 锁定的文件会阻止 Linux 系统中某些文件或者数据的访问,这个概念也存在于 Windows 或者其他的操作系统 ...

  6. java延迟队列

    大多数用到定时执行的功能都是用任务调度来做的,单身当碰到类似订餐业务/购物等这种业务就不好处理了,比如购物的订单功能,在你的订单管理中有N个订单,当订单超过十分钟未支付的时候自动释放购物车中的商品,订 ...

  7. vue框架中什么是MVVM

    前端页面中使用MVVM的思想,即MVVM是整个视图层view的概念,属于视图层的概念. MVVM是前端视图层的分层开发思想,将页面分成了Model, View,和VM:其中VM是核心,因为VM是V和M ...

  8. JavaSE---基本数据类型存储大小

  9. spring框架的一些测试思路

    一.Spring Boot Actuators Spring Boot Actuator是Spring Boot提供的对应用系统的监控和管理的集成功能,可以查看应用配置的详细信息,例如自动化配置信息. ...

  10. Devops、CI\CD、Jenkins

    Devops DevOps对应用程序发布的影响 在很多企业中,应用程序发布是一项涉及多个团队.压力很大.风险很高的活动.然而在具备DevOps能力的组织中,应用程序发布的风险很低,原因如下 [2] : ...