simplexml 使用实例
搞了几天php处理xml文件,终于有点头绪,记录下来分享一下。
simplexml 是php处理xml文件的一个方法,另一个是dom处理,这里只说simplexml。
目前php处理xml用的比较多,比较成熟的还是dom。但dom在速度和代码量上还是比较受人诟病的。
simplexml的有些函数需要比较高版本的php,建议是php5.2以上。
一,新建xml
方法1(普通新建文件):
$fp=fopen('111.xml','w');
$xmlContent='<?xml version="1.0" encoding="utf-8" ?> ';
$xmlContent.='<navlist>';
$xmlContent.='<nav id="1">';
$xmlContent.='<name>我我我</nname> ';
$xmlContent.='<color>#000000</ncolor> ';
$xmlContent.='</nav>';
$xmlContent.='<nav id="2">';
$xmlContent.='<name>你你你</nname> ';
$xmlContent.='<color>#ffffff</ncolor> ';
$xmlContent.='</nav>';
$xmlContent.='<nav id="3">';
$xmlContent.='<nname>他他他</nname> ';
$xmlContent.='<ncolor>#cccccc</ncolor> ';
$xmlContent.='</nav>';
$xmlContent.='</navlist>';
fwrite($fp,$xmlContent);
fclose($fp);
方法2(使用simplexml新建):
$fp=fopen(111.xml','w');
$xmlContent='<?xml version="1.0" encoding="utf-8" ?><navlist></navlist>';
fwrite($fp,$xmlContent);
fclose($fp);
$xml=simplexml_load_file('111.xml');
$app=$xml->addChild('nav');
$app->addAttribute('id',"1");
$app->addChild('name','我我我');
$app->addChild('color','#000000');
$app=$xml->addChild('nav');
$app->addAttribute('id',"2");
$app->addChild('name','你你你');
$app->addChild('color','#ffffff);
$app=$xml->addChild('nav');
$app->addAttribute('id',"3");
$app->addChild('name','他他他');
$app->addChild('color','#cccccc);
$xml->asXML('111.xml');
可以看到第一种方法其实只是普通的写入文件,第二种方法才是使用simplexml新建xml文件,但为什么我要把第一种普通方法放前面呢?因为本人测试了一下两种方法的运行时间,发现第一种方法使用的时间更少,所以建议使用普通的新建文件方法新建xml文件就行了!
新建的文件结构如下:
<?xml version="1.0" encoding="utf-8"?>
<navlist>
<nav id="1">
<name>我我我</nname>
<color>#000000</ncolor>
</nav>
<nav id="2">
<name>你你你</nname>
<color>#ffffff</ncolor>
</nav>
<nav id="3">
<name>他他他</nname>
<color>#cccccc</ncolor>
</nav>
</navlist>
二,读取xml
if($xml=simplexml_load_file('111.xml')){
foreach($xml->nav as $list){
$value[]=get_object_vars($list);
}
}else{
echo('load xml error!');
}
get_object_vars函数是把对象转化为数组,这样$value就是一个Php的普通数组了,大家可以print_r一下这个数组,Php数组的读取就不用我说了吧。
三,修改xml
1,新建节点
$xml=simplexml_load_file('111.xml');
$app=$xml->addChild('nav');
$app->addAttribute('id',"4");
$app->addChild('name','它它它');
$app->addChild('color','#000000');
$xml->asXML(111.xml');
将追加作为最后一个节点
2,修改节点
$xml=simplexml_load_file('111.xml');
$xg=$xml->xpath("/navlist/nav[@id=‘3’]");
$xg[0]->name="他啊他啊";
$xg[0]->color="#444444";
$xml->asXML(111.xml');
修改了nav属性id为3的节点
3,删除节点
删除节点用unset函数就行,但要删除指定节点比较麻烦,删除指定节点只能使用节点的索引值。
比如删除第二个节点(id为2的):
$xml=simplexml_load_file('111.xml');
unset($xml->nav[1]);
$xml->asXML(111.xml');
索引从0开始,所以第二个节点的索引是1。
如果想通过指定的属性删除,则需要循环所有节点获取指定属性的节点索引
如下:
$xml=simplexml_load_file('111.xml');
$i=0;
foreach($xml as $dup){
$sc=$dup->attributes();
if($sc['id']==2){
unset($xml->nav[$i]);
}
$i++;
}
$xml->asXML('111.xml');
四,删除xml
if(file_exists('111.xml')){
unlink('111.xml');
}
data.dtd 定义XML合法词汇
data.xml XML文档
index.php 主索引文件
--------------------------------------------------------------------------------
代码如下:
data.dtd
data.xml
index.php
注意:
data.xml文件是GBK编码
index.php文件是GBK编码
但是simpleXML解析出来的是UTF-8编码
所以就使用到iconv()函数
用到一个简写的转换函数minIconv()
function minIconv($str, $in='utf-8', $out='gbk'){
$str = iconv($in, $out, $str);
return $str;
}
-----------------------------------------------------------
$data = array();
$data['node_type'] = 3;
$data['node_name'] = (string)$targetNode->node_name[0];
$data['node_id'] = (string)$targetNode->target_node->node_id[0];
echo json_encode(array('success' => true,'data' => $data));
--------------------------------------------------------------------------------------------------------------------------------------------------
SimpleXML
介绍
SimpleXML提供了一种简单,直观的方法来处理XML。它只有一个单一类型的类,三个函数和六个方法。
使用SimpleXML
SimpleXMLElement 类是这个扩展中所有操作的核心类。可以用new关键字直接创建这种类,或是使用simplexml_load_file()或 simplexml_load_string()函数返回这种类。本文将使用清单7-1的XML文档来说明如何使用SimpleXML,将此文档命名为 sml.xml。
清单7-1 sml.xml
创建一个SimpleXMLElement对象
使用new关键字创建
使用simplexml_load_string()创建
如何选择这两种创建SimpleXMLElement的方法呢?simplexml_load_string()提供了更多的函数,比如控制解析选项的能力。如果不需要这些额外的函数的话就可以凭个人爱好选择一种方法。
使用simplexml_load_file()从一个URI创建
simplexml_load_string()和simplexml_load_file()都有一个必需的参数和可选的参数。从PHP5.1开始simplexml_load_file()多了一个用来控制解析行为的第三个参数。
保存XML数据
与DOM扩展一样,SimpleXML也提供了一个用来输出XML内容的方法asXML()。可以用这个方法以字符串或文件形式输出这个文档或文档中的某个节点。
输出:
访问元素节点
在SimpleXML中,可以直接通过元素的名称来访问特定的元素。
访问元素
当一个文档被载入SimpleXML时,文档被看成是一个SimpleXML对象,文档中的所有元素都被看成是该对象的属性。
如果使用DOM来访问title,代码如下
显然SimpleXML对的起它的名字。
访问内容
这段代码检查了两个SimpleXMLElement对象,$author和$title。两者的区别是$author元素有包含子元素而$title元素只包含一个文本节点。
输出如下:
object(SimpleXMLElement)#4 (1) {
[0]=>
string(18) “SimpleXML in PHP 5″
}
Title: SimpleXML in PHP 5
object(SimpleXMLElement)#6 (2) {
[”firstname”]=>
string(3) “Rob”
[”surname”]=>
string(8) “Richards”
}
Author:
检查输出结果可以发现,$title是一个包含有文本内容的SimpleXMLElement对象,索引0表示元素的文本内容,当打印$title时,文本内容将以字符串形式返回。
$author元素有两个子元素,从输出结果可以看出,这些子元素被看成SimpleXMLElement对象的属性,这些属性的值是它们对应的节点包含的内容。用print输出$author时,输出结果是空格和换行符。
如果一个元素无子元素,只包含文本内容,那么可以将此元素所对应的SimpleXMLElement对象视为一个字符串来使用,有些情况下,为了获得以字符串形式返回文本内容,必须执行类型转换操作:
有 子元素的元素所对应的SimpleXMLElement对象将返回该对象直属的文本节点,而不是任一子元素的内容。如果用print输出$author, 将得到一个27字符长度,包含空格和换行符的字符。 Neither of the child elements,firstname or surname, nor their content is returned in the string.
为了理解最后一点,可以运行一些下面这段代码:
使用迭代对象
SimpleXMLElement 对象在大多数情况下是可迭代的,可以用这个特性来访问文档中多个元素名一样的节点,如清单7-1中的para元素。在使用元素名作为属性来访问一个元素 时,SimpleXMLElement对象不是一个单一节点的存取器,它实际上是作为属性访问的元素名节点的集合。直接利用元素名作为属性访问实际上访问 访问这个集合中的第一个元素。
看下这段代码
这 段代码中,$para变量就是para元素的集合,实际上包含了两个元素。如果直接访问$para的话实际上是访问第一个para元素,利用迭代可以看出 $para所包含的内容是两个元素。其中CDATA节点被看做是纯文本的内容,其中包含的空格和换行符都会被如实输出。
用迭代的方式来访问所有的元素显然不太实际,有时候我们希望访问结果集中的某个特定的元素,这时可以使用从0开始的索引来访问这个结果集,例如:
运行这段代码会发现foreach循环失效了,这是因为SimpleXML知道你只是在寻找结果集中某个特定的元素,这种情况下对象是不可迭代的。
Caution:使用索引方式访问一个SimpleXMLElement对象会返回一个不可以迭代的对象,因为它是一个单一的元素而不是一个元素集。
访问未知元素
在不知道XML文档的结构的情况下可以利用SimpleXML中的children()方法来返回一个可以迭代方式访问的某个元素的所有子元素的SimpleXML对象。如:
上述代码用children()方法返回了author节点下的所有子元素,然后用foreach循环输出。也可以使用索引方式访问返回的子元素,如echo $children[1];。
理解PHP对象函数
SimpleXMLElement对象的属性是动态的,因为这些属性是由对象实例决定的,而不是由类本身决定的。在PHP中,可以利用get_object_vars()函数来返回某个对象的所有属性,返回的结果是一个包含属性和值的数组,如:
输出:
firstname: Rob
surname: Richards
这段代码访问的子元素都只包含文本内容,所以返回的数组只包含属性名和值,对于一个包含许多子节点的元素,返回的结果稍微复杂点:
输出:
array(3) {
[”title”]=>
string(18) “SimpleXML in PHP 5″
[”author”]=>
object(SimpleXMLElement)#4 (2) {
[”firstname”]=>
string(3) “Rob”
[”surname”]=>
string(8) “Richards”
}
[”copyright”]=>
object(SimpleXMLElement)#5 (2) {
[”year”]=>
string(4) “2005″
[”holder”]=>
string(12) “Rob Richards”
}
}
使用DOM互操作
另一种访问未知元素的方法是使用DOM,可以将一个节点导入DOM扩展,然后使用DOM的属性和方法来处理。
将节点导入DOM扩展时并没有创建一个节点的副本(copy),而是直接访问导入的节点(JIMMY注:这个概念很重要)。
修改内容
利用SimpleXML修改元素内容非常方便,你可以改变或移除树中的某个元素,但是不能直接在树中添加一个元素。要添加一个元素,可以使用DOM的互操作性:
输出:
编辑文本内容
可以利用SimpleXML的属性赋值方法来直接编辑一个元素的内容,要主意的是如果文档中有多个元素名一样的元素,如果没有使用索引来指定要编辑哪个元素时PHP将发出一个警告。如:
输出:
Warning: main() [/phpmanual/function.main.html]: Cannot assign to an array of nodes
(duplicate subnodes or attr detected)
必须给para指定索引告诉程序你要编辑哪个元素:
输出:
这样,第二个para元素的内容被改为Removed CDATA。如果要编辑一个在文档中唯一存在的元素可不必指定索引,直接修改。如修改title:
输出:
强烈建议使用索引来编辑元素,除非你对文档的结果非常确定。使用索引来编辑title元素会比较安全,如$book->chapter->title[0] = “New Title”;这行代码用索引[0]指定要编辑第一个title。
编辑有子树的元素
输出:
这 段代码中,首先将文档中的holder元素赋值给$cholder变量,然后打印该变量。bookinfo元素包含有title,author和 copyright子树,它的内容被字符串No Book Info代替,从bookinfo的输出结果可以看出,它的子树被清空并且被字符串代替了。接着试图再次打印$cholder变量的XML内容,程序输出 一个警告,这个变量依然是一个SimpleXMLElement对象,但它所属的节点在bookinfo元素改变时已经被破坏了。
另一种情况。将bookinfo元素中的子元素用字符串 代替。
如果你认为上述代码将bookinfo中的内容清空后再给bookinfo创建了一个子节点title,那么你错了。输出结果是
<title>SimpleXML in PHP 5</title>
实际上bookinfo元素的子元素都被移除了,但是新赋值的XML数据被转义成文本内容,而不是一个新的子元素。
如果想用一个子树代替另一个子树,可以利用DOM扩展:
输出结果:
移除元素
可以用PHP内置函数unset()来将一个元素从树中移除。unset()的参数必须是一个SimpleXMLElement,用属性方法来访问要移除的元素。例如,从chapter节点移除title元素:
上述代码执行后,chapter的结构为:
将这个结果与下面代码执行的结果想比较:
输出结果
title元素没有被移除,unset函数只对$title变量作用并没有将title元素从树中移除。
在移除一个元素时必须注意,用索引来指定的特定元素不会被移除:
输出:
如果要移除所有的para元素时可以利用下面代码:
输出:
问题是如果你只想移除其中的一个para元素时要怎么办。这时可以再次用到DOM扩展:
输出:
所幸的是PHP5.2开始已经支持删除用索引指定的元素了:
访问属性
读取属性
下面的代码输出了book元素中的lang属性
访问用索引指定的元素的属性:
在不知道属性名的情况下可以用attributes()方法来输出属性:
如果要获得位置的属性名,可以使用DOM扩展:
修改属性
修改属性的值跟修改元素的值一样,直接对其赋值就可以了:
添加一个属性也很简单,如果对一个不存在的属性名进行赋值就给元素创建一个新属性
输出:
移除属性
移除属性也用到unset()函数:
输出:
扩展SimpleXMLElement类
当实例化扩展的类时,文档的每个节点对象的类型都是扩展类的类型。
输出:
使用new方法可以用来处理字符串类型的XML,如果XML保存在一个文件中,那么可以将扩展的类名作为第二个参数传给simplexml_load_string或simplexml_load_file
输出结果与用new关键字输出的结果一样。
在SimpleXML中使用命名空间
将清单7-1的内容改为
如果试图用普通的方法来访问元素或属性,你会分析这并不可行,例如:
输出的结果是两行空白。
在 访问命名空间节点前,必须使用children()和attributes()方法。这两个方法不仅可以在没有指定参数的时候使用,也可以在指定一个 URI命名空间作为参数使用。如果一个SimleXMLElement对象是从这两个方法返回的,那么你就可以像普通的元素和属性一样访问命名空间下的元 素和属性:
children ()和attributes()方法可以被看做是过滤器,如果没有参数或者传递一个NULL作为参数,这两个方法将返回非命名空间下的元素或属性;否则将 返回特定命名空间下的元素或属性。在重置之前,命名空间仍然起作用并且被子节点继承。例如,使用$bookinfo被设置为命名空间 http://www.example.com/ns1的对象,可以使用 print $bookinfo->author->firstname来但因author中的firstname元素。所有的元素都在命名空间下,因此 你在创建$bookinfo对象时不必一直使用children()设置命名空间。