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. [Fw]初探linux中断系统(2)

    初探linux中断系统(2) 中断系统初始化的过程 用来初始化中断系统的函数位于arch/x86/kernel/irqinit.c,定义如下 void __init init_IRQ(void){ i ...

  2. 初始 vue

    1.js,jQuery编程范式:命令式编程 vue编程范式:声明式编程 v-for   遍历数组内容 v-on: click   监听点击事件,语法糖 @click el: 类型:string | H ...

  3. java synchronized的四种用法

    一 修饰方法 Synchronized修饰一个方法很简单,就是在方法的前面加synchronized,synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来 ...

  4. send, sendto, sendmsg - 从套接字发送消息

    概述 #include <sys/types.h> #include <sys/socket.h> int send(int s, const void *msg, size_ ...

  5. 如何解决“ VMware Workstation 不可恢复错误: (vcpu-0) vcpu-0:VERIFY vmcore/vmm/main/cpuid.c:386 bugNr=1036521”

    第一次装虚拟机,装centos7遇到的坑: 1. 出现 “VMware Workstation 不可恢复错误: (vcpu-0) vcpu-0:VERIFY vmcore/vmm/main/cpuid ...

  6. shell unique

    由于uniq命令只能对相邻行进行去重复操作,所以在进行去重前,先要对文本行进行排序,使重复行集中到一起 1.文本行去重 (1)排序由于uniq命令只能对相邻行进行去重复操作,所以在进行去重前,先要对文 ...

  7. C++的命名空间

    作用:防止类,函数,变量等之间重名,比如在代码合并的时候 假如两个头文件中均定义了类Cal,而调用程序同时包含了两个头文件,当在定义Cal c时,程序会报类型重定义的错误.这种问题可以通过命名空间来解 ...

  8. Es学习第六课, ES基本搜索_search

    前面几课ES的基本概念.安装和分词都讲过了,下面我们就来实战一下ES的核心功能-搜索,这节课我们主要讲的是基本搜索 _search(注意:ES的关键字都要加前缀_,所以我们在定义索引.类型名称时不要带 ...

  9. Concurrent - 多线程

    原创转载请注明出处:https://www.cnblogs.com/agilestyle/p/11426916.html Java中有几种方法可以实现一个线程? 继承Thread类(不支持多继承) 实 ...

  10. leetcode-第14周双周赛-1273-删除树节点

    题目描述: 自己的提交:动态规划 class Solution: def deleteTreeNodes(self, nodes: int, parent: List[int], value: Lis ...