XML严禁 & < ,建议 < > & ‘ “进行转义
XML中预定义了5个实体引用: < > & ‘ “
其中,‘<‘ 和 ‘&‘ 是非法的。
‘<‘ 会产生错误,因为解析器会把该字符解释为新元素的开始。
‘&‘ 也会产生错误,因为解析器会把该字符解释为字符实体的开始。
其它实体虽然都是合法的,但是把它们替换为实体是个好的习惯。
CDATA区段中值的限制
CDATA区段由 ‘<[CDATA[‘ 开始,由 ‘]]>‘ 结束.因此,CDATA的值不能包含‘]]>‘,否则XML解析器会提前闭合CDATA,导致解析错误.而结尾的‘]]>‘闭合也不能包含空格或者折行.
<![CDATA[ value ]]> ]]> //值包含']]>',非法 <![CDATA[ value ] ] > //结尾的']]>'包含空格,非法 <![CDATA[ value ]]> //合法
SimpleXML类生成带有CDATA区段的值
SimpleXML类如其名字,因为其简单那易用性,得到一部分程序员的青睐,但是其本身却不支持生成带有CDATA区段的值.是不是为了生成CDATA区段的值就要放弃SimpleXML类了呢?
谷歌到一种解决方案
class SimpleXMLExtended extends SimpleXMLElement { public function addCData($cdata_text) { $node = dom_import_simplexml($this); $no = $node->ownerDocument; $node->appendChild($no->createCDATASection($cdata_text)); } } $xmlFile = 'config.xml'; // instead of $xml = new SimpleXMLElement('<sites/>'); $xml = new SimpleXMLExtended('<sites/>'); $site = $xml->addChild('site'); // instead of $site->addChild('site', 'Site Title'); $site->title = NULL; // VERY IMPORTANT! We need a node where to append $site->title->addCData('Site Title'); $site->title->addAttribute('lang', 'en'); $xml->asXML($xmlFile);
此类继承自SimpleXMLElement类,通过添加addCData方法,利用DOMDocument和SimpleXML的互通性,把SimpleXML对象转换成DOM元素对象,利用DOM元素对象节点可以创建CDATA区段值的特点完成操作.
此类虽然解决了SimpleXML不能创建CDATA区段值的缺点,但是代码不够简洁--每次都要在设置值之前都要先将值设置为NULL,而此方法又是必须的,否则会报错.
因此我对此类进行了再次封装,调用起来更加简洁,代码如下:
class SimpleXMLExtended extends SimpleXMLElement { public function addCData($data) { $node = dom_import_simplexml($this); $no = $node->ownerDocument; $node->appendChild($no->createCDATASection($data)); } public function addNode($key,$value=''){ if($value === '') $value = ' '; $this->$key = NULL; $this->$key->addCData($value); } } //调用更加简洁 $xml = new SimpleXMLElement('<?xml version="1.0"?><DOCUMENT></DOCUMENT>'); $xml->addChild('item');//向XML节点添加一个子节点item $item->addNode('key','value');//向item节点添加一个键为"key",CDATA区段值为"value"
UTF-8编码下提示非UTF-8字符
XML中编码全是UTF-8,怎么会有非UTF-8字符呢?通过浏览器查看源码,也没发现非法字符.
问大神同事,得到了‘没遇到过‘的答案.
然后就想着把报错的数据字段从数据库里复制出来,看一下结果.从库中复制到了sublime中,然后看到如下图的‘FS‘字符
好端端的内容,为啥会产生一个‘FS‘呢?然后谷歌,终于找到了答案.原来该特殊字符叫‘控制字符‘,想要了解控制字符的同学可以看关于控制字符的定义.
当时解决方法简直简单粗暴,手动删掉,然后回存到数据库:
后来领导也遇到相似的问题,我告诉他是有可能是控制字符造成的,然后他给了我一个函数,可以过滤掉控制字符.函数如下:
function strip_control_characters($string){ return preg_replace('/[\x00-\x1F\x7F-\x9F]/u', '', $string); }
PS: 此函数需要编码为UTF-8才可以过滤掉.
XML读取的字段中包含‘-‘,要用‘{}‘把字段包裹
$arr = array('a-b-c'=>'abc'); $obj = (object)($arr); //如何获取'a-b-c'字段的值? echo $obj->a-b-c;//报错 echo $obj->'a-b-c';//报错 echo $obj->{'a-b-c'};//正常
** 此注意点可以总结为PHP在获取对象的key中包含‘-‘,要用‘{}‘包住 **
SimpleXML的节点值要强转成字符型后再比较
$string = <<<XML <?xml version='1.0'?> <document> <title>Dear Jane</title> <from>Joe</from> <to>Jane</to> <body> Dear Jane: I love U,can you be my girlfriend? yours Joe </body> </document> XML; $email = simplexml_load_string($string); //Joe发送邮件,但是忘记收件人是否是女神Jane if($email->to == 'Jane'){ echo 'Good Job'; }else{ echo 'I am in troblem!'; }
得到的答案是‘I am in troblem!‘
原来‘$email->to‘得到的值的类型为SimpleXMLElement对象,
非字符串,非数组.
因此,一定要强制转换成‘string‘类型后在比较.
XML调试
因为SimpleXML类和DOMDocument类都是基于libxml扩展开发的,开发过程中,可以使用libxml_use_internal_errors调试.
此函数有一个参数,默认为false,即禁用用户错误处理.true为开启用户错误处理.
libxml_use_internal_errors(true); $sxe = simplexml_load_string("<?xml version='1.0'><broken><xml></broken>"); if ($sxe === false) { echo "Failed loading XML\n"; foreach(libxml_get_errors() as $error) { echo "\t", $error->message; } }
以上代码输出:
Failed loading XML
Blank needed here
parsing XML declaration: ‘?>‘ expected
Opening and ending tag mismatch: xml line 1 and broken
Premature end of data in tag broken line 1
通过错误处理,知道原来有两个错误:
声明少写一个 ‘?>‘
xml标签没有闭合