使用DOM读取XML值

来源:互联网 发布:中日友好靠韩国 知乎 编辑:程序博客网 时间:2024/04/26 16:13

原文:http://developer.51cto.com/art/200907/135051.htm

DOM树定义了文档的逻辑结构,以及控制你访问和操作这些文档的方法。使用DOM,开发人员可以创建XML或HTML文档,操作它们的结果,增加、修改和删除文档 元素及内容。可以从任何编程语言访问DOM,本文使用PHP 5 DOM扩展,它是PHP核心的一部分,因此除了PHP外,不需要安装其它软件。

DOM树节点遵循XML命名规范,如:

1、Document节点 -- 表示DOMDocument接口

2、Element节点 -- 表示DOMElement接口

3、Attribute节点 -- 表示DOMAttr接口

4、Comment节点 -- 表示DOMComment接口

5、Text节点 -- 表示DOMText接口

提取元素

这一小节介绍如何从DOM树中提取元素和值,本文使用Book.xml作为例子进行说明,其内容如清单1所示。

清单1 Book.xml

  1. <?xml version="1.0" encoding="UTF-8" standalone="yes" ?> 
  2. <book> 
  3. <!--XML Processing [part I] --> 
  4.  <name>XML Processing I</name> 
  5.   <author>John Smith Jr.</author> 
  6.   <publisher>HisOwnTM</publisher> 
  7.   <ISBN>111-222-333-4441</ISBN> 
  8.   <contents> 
  9.     <chapter_I> 
  10.       <title>What is XML about ?</title> 
  11.       <content>XML (Extensible Markup Language) is a ...</content> 
  12.     </chapter_I> 
  13.     <chapter_II> 
  14.       <title>SAX</title> 
  15.       <content>SAX is a simple API for ...</content> 
  16.    </chapter_II> 
  17.     <chapter_III> 
  18.       <title>StAX</title> 
  19.       <content>Much powerful and flexible, StAX, is very...</content> 
  20.     </chapter_III> 
  21.     <chapter_IV> 
  22.       <title>DOM 
  23.         <subtitle>DOM concept  
  24.           <continut>Starting to use DOM...</continut> 
  25.         </subtitle> 
  26.         <subchapter_IV_I> 
  27.          <title>First DOM application...</title> 
  28.           <content>Here it is your first DOM application...</content> 
  29.         </subchapter_IV_I>  
  30.       </title>              
  31.     </chapter_IV>           
  32.     <end>The end...</end>           
  33.   </contents> 
  34. <!-- See you in XML Processing [part II] --> 
  35. </book> 

先下载本文使用的PHP代码压缩包,http://assets.devx.com/sourcecode/41975_oa_mainsource.zip,将Book.xml和压 缩包解压后放在同一个目录下。

第一个示例应用程序使用Book.xml文档,提取出关联的树,然后使用DOMElement接口的getElementsByTagName方法显示第一个子节点实例:

DOMNodeList DOMElement::getElementsByTagName(string $name):这个方法返回所有$name参数指定的标签名的子元素。下面的例子查找<book>根节点 ,然后查找它的子节点 <author>,<publisher>和 <name>元素,选择每个子节点的第一个,最后打印这些节点的值:

  1. <?php 
  2.  // 创建一个文档实例  
  3.   $doc = new DOMDocument();  
  4.   //载入Book.xml文件  
  5.   $doc->load( 'Book.xml' );  
  6.   //使用book标签名搜索所有元素  
  7.   $books = $doc->getElementsByTagName( "book" );  
  8.   //使用author标签名搜索所有元素  
  9.   $authors = $doc->getElementsByTagName( "author" );  
  10.   //返回第一个标签名为author的元素  
  11.   $author = $authors->item(0)->nodeValue;  
  12.   //以publisher标签名搜索所有元素 
  13.   $publishers = $doc->getElementsByTagName( "publisher" );  
  14.   //返回第一个找到的标签名为publisher的元素  
  15.    $publisher = $publishers->item(0)->nodeValue;  
  16.   //搜索标签名为name的所有元素 
  17.   $titles = $doc->getElementsByTagName( "name" );  
  18.   //返回标签名为name的第一个找到的元素  
  19.   $title = $titles->item(0)->nodeValue;  
  20.   //打印找到的值  
  21.   echo "$title - $author - $publisher \n";  
  22.  ?> 

最后一行是打印第一个标题,第一个作者,第一个出版商,使用连字符分隔,输出:

XML Processing I - John Smith Jr. - HisOwnTM

递归浏览DOM树

因为XML文档结构中一个标签可以包括另一个标签(分支树),剩下就是叶子节点,因此你可以浏览完整的树或从任何节点开始递归浏览子树 。下面的例子是从任何开始节点($node)浏览下面的XML子树,并列出节点的名字和值。

  1. function getNodesInfo($node)  
  2. {  
  3.    if ($node->hasChildNodes())  
  4.    {  
  5.       $subNodes = $node->childNodes; 
  6.       foreach ($subNodes as $subNode)  
  7.       { 
  8.          if (($subNode->nodeType != 3) ||   
  9.             (($subNode->nodeType == 3) &&  
  10.             (strlen(trim($subNode->wholeText))>=1)))     
  11.          {  
  12.             echo "Node name: ".$subNode->nodeName."\n";  
  13.             echo "Node value: ".$subNode->nodeValue."\n";  
  14.          }  
  15.          getNodesInfo($subNode);           
  16.       }  
  17.    }        
  18. }    

上面的例子使用下面的条件去除了所有空文本节点,让输出看起来更干净:

  1. if (($subNode->nodeType != 3) ||   
  2.    (($subNode->nodeType == 3) &&  
  3.    (strlen(trim($subNode->wholeText))>=1)))    

前面的代码检查节点是否被处理,同样,你可以设置预定义的preserveWhiteSpace属性,它移除冗余的空白,默认值是TRUE。

为了测试这个功能,下面这个例子传递Book.xml文档的根节点给递归函数getNodesInfo,然后打印出整个DOM树的标签和值:

  1. <?php 
  2. //创建一个文档实例   
  3. $doc = new DOMDocument();  
  4. //载入Book.xml文件  
  5. $doc->load( 'Book.xml' );   
  6. //设置对象树根   
  7. $root = $dom->firstChild;  
  8. // 递归函数列出子树的所有节点  
  9. function getNodesInfo($node)  
  10. {  
  11.    if ($node->hasChildNodes())  
  12.    {  
  13.       $subNodes = $node->childNodes;  
  14.      foreach ($subNodes as $subNode)  
  15.       {  
  16.          if (($subNode->nodeType != 3) ||   
  17.             (($subNode->nodeType == 3)    
  18.             &&(strlen(trim($subNode->wholeText))>=1)))     
  19.          {  
  20.          echo "Node name: ".$subNode->nodeName."\n";  
  21.          echo "Node value: ".$subNode->nodeValue."\n";  
  22.      }  
  23.       getNodesInfo($subNode);           
  24.       }  
  25.    }        
  26. }     
  27. //调用getNodesInfo函数  
  28. getNodesInfo($root);  
  29. ?> 

显示输出的小部分内容

 图1显示了输出的小部分内容

图- 1文档内容:这个图显示了通过getNodesInfo递归函数运行Book.xml的部分输出内容

增加新节点

DOMNode接口包括多个创建新节点和在DOM树中插入节点的方法,如果要创建一个新节点,可以使用createElement或createTextNode方法,然后 ,为了增加一个新节点到DOM树上,可以调用appendChild或insertBefore方法,appendChild方法增加一个新的子节点到特定节点的子节点列表的后面,而 insertBefore方法是在特定节点的前面插入一个节点。

下面是这些方法的原型:

1、DOMElement createElement(string $name [, string $value ]) :这个方法创建了一个DOMElement类的实例,$name参数表示新元素的标签名,$value参数 表示元素的值,你也可以稍后使用DOMElement->nodeValue属性其值。

2、DOMText createTextNode(string $content):这个方法创建了一个DOMText类的实例,$content参数表示新的文本节点的文本内容。

3、DOMNode DOMNode::appendChild(DOMNode $newnode):这个函数扩展了现有子节点末尾$newnode参数,或创建一个新的包括指定节点的子节点列表。

4、DOMNode DOMNode::insertBefore(DOMNode $newnode [,DOMNode $refnode]):这个方法在$refnode节点前插入$newnode参数,如果$refnode节点丢失,新的 节点就添加到节点的子节点列表前。

下面的例子创建了一个<bibliography>节点,并将其追加到节点的末尾:

  1. //创建一个新元素   
  2. $newElement = $dom->createElement('bibliography','Martin Didier, Professional XML');  
  3. //使用appendChild函数将其追加到根节点  
  4. //调用appendChild函数  
  5. appendNewChild($root,$newElement);  
  6. //这个函数追加了一个新的子节点  
  7. function appendNewChild($currentNode, $node)  
  8. {  
  9.    $currentNode->appendChild($node);  

getNodeInfo()函数运行结果

如果你通过getNodeInfo()函数运行得出结果,你将会看到如图2所示的输出。

图- 2 追加的节点:这个图显示了新加的<bibliography>节点和它的内容

下面的例子是在<publisher>节点增加一个<foreword>子节点:

  1. //创建一个新的<foreword>元素  
  2. $newElement = $dom->createElement('foreword',  
  3.    'What I love about this book is that it '.  
  4.    'grew out of just such a process, '.  
  5.    'and shows it on every page.');  
  6. //设置引用节点  
  7. $allContents = $dom->getElementsByTagName('publisher');  
  8. $contents = $allContents->item(0);  
  9. //调用insertNewChild函数   
  10. insertNewChild($contents,$newElement);  
  11. //这个函数插入一个新的子节点作为 $currentNode的第一个子节点  
  12. function insertNewChild($currentNode, $node)  
  13. {  
  14.    $currentNode->insertBefore(  
  15.       $node, $currentNode->firstChild);     

显示新的节点

通过getNodesInfo运行修改后的文档,显示新的节点,如图3所示。

图- 3 插入节点:这张图片显示了在<publisher>节点前插入了<foreward>子节点

节点克隆

克隆一个节点意味着创建一个和当前节点一模一样的节点,克隆节点时使用cloneNode方法。

DOMNode DOMNode::cloneNode([ bool $deep]):创建当前的克隆,$deep参数指定是否要拷贝当前节点的子节点,其默认值是false。下面的示例代码显示克隆 <author>元素,并作为源<author>元素的子节点,图4显示了输出内容:

  1. //设置引用节点  
  2. $author = $root->getElementsByTagName('author')->item(0);  
  3. //调用cloningNode函数  
  4. cloningNode($author);   
  5. //这个函数克隆$currentNode  
  6. function cloningNode($currentNode)  
  7.    {  
  8.       $clonenode = $currentNode -> cloneNode(true);  
  9.       $newnode = $currentNode->appendChild($clonenode);     
  10.    } 

克隆节点

图- 4 克隆节点:克隆<author>子节点并将其放在源<author>节点下,源节点的文本值变成两份了,因为检索节点的文本值时也检索了其子节点的 值


移除子节点

使用removeChild方法从DOM树中移除子节点。

DOMNode DOMNode::removeChild(DOMNode $oldnode):这个函数移除一个子节点,$oldnode参数指出要移除的子节点。下面的示例代码从Book.xml文档中移除子 节点,从图5中显示的输出内容可以看到bibliography节点消失了。

移除节点

图- 5 移除节点:移除最后一个子节点<bibliography>后,重新列出了节点的名称值,显示节点确实被移除了

替换节点

为了用一个新节点替换已有的节点,使用replaceChild方法。

DOMNode DOMNode::replaceChild(DOMNode $newnode, DOMNode $oldnode):这个函数使用$newnode子节点替换$oldnode节点。

例如,假设你想用新的code子节点替换ISBN子节点:

  1. //获取ISBN节点  
  2. $element = $dom->getElementsByTagName('ISBN')->item(0);   
  3. //创建新的<code>元素   
  4. $code = $dom->createElement('code', '909090');  
  5. //调用replacingNode函数   
  6. replacingNode($code,$element);  
  7. //这个函数使用$node替换$currentNode   
  8. function replacingNode($currentNode, $node)  
  9. {  
  10.    $node->parentNode->replaceChild($currentNode, $node);     

替换节点

图6中显示的输出内容表明节点被替换了

图- 6 替换节点:这里显示的是用新的<code>节点替换<ISBN>节点后的文档内容

导入节点

使用importNode方法从另一个树拷贝一个节点到当前的树。

DOMNode DOMDocument::importNode(DOMNode $importedNode [,bool $deep]):这个方法从另一个XML文档导入一个节点,然后插入当前文档的DOM树中, $importedNode参数指出了要导入的节点,导入的节点表示原始节点的一份拷贝,因此导入操作不会修改外部树,$deep参数控制是否导入被导入节点的深度,值为 TRUE时,导入完整的节点子树,为FALSE时,只导入节点本身。

下面的示例从Book_continue.xml文件导入<continue>节点到Book.xml,下面是Book_continue.xml文档的内容:

  1. <?xml version="1.0" encoding="UTF-8" standalone="yes" ?> 
  2. <!--chapter V--> 
  3. <continue> 
  4.   <chapter_V> 
  5.    <title>XPath</title> 
  6.    <content>XPath is language for...</content> 
  7.   </chapter_V> 
  8.   <![CDATA[  
  9.      This chaper is a bonus to...  
  10.      ]]> 
  11.   <printing cap_I="click_here_for_chapter_I"   
  12.     cap_II="click_here_for_chapter_II"     
  13.     cap_III="click_here_for_chapter_III" 
  14.     cap_IV="click_here_for_chapter_IV"   
  15.     cap_V="click_here_for_chapter_V" /> 
  16. </continue> 
  17. 下面是导入<continue>节点的代码:  
  18. <?php 
  19. $olddoc = new DOMDocument;  
  20. $olddoc->load("Book_continue.xml");  
  21. //我想导入到一个新文档的节点  
  22. $node = $olddoc->getElementsByTagName("continue")->item(0);  
  23. $newnewdoc = new DOMDocument;  
  24. $newdoc->formatOutput = true;  
  25. $newdoc->load("Book.xml");  
  26. //导入节点及其所有子节点到文档  
  27. $node = $newdoc->importNode($node, true);  
  28. //然后追加到根节点  
  29. $newdoc->documentElement->appendChild($node);  
  30. echo "\nThe 'new document' after copying the nodes into it:\n";  
  31. $root = $newdoc->firstChild;  
  32. function getNodesInfo($node)  
  33. {  
  34.    if ($node->hasChildNodes())  
  35.    {  
  36.       $subNodes = $node->childNodes;  
  37.       foreach ($subNodes as $subNode)  
  38.       {  
  39.          if (($subNode->nodeType != 3) ||   
  40.             (($subNode->nodeType ==3) &&  
  41.             (strlen(trim($subNode->wholeText))>=1)))     
  42.          {  
  43.          echo "Node name: ".$subNode->nodeName."\n";  
  44.          echo "Node value: ".$subNode->nodeValue."\n";  
  45.       }  
  46.       getNodesInfo($subNode);           
  47.       }  
  48.    }        
  49. }  
  50. getNodesInfo($root);     
  51. ?> 

导入节点

图7显示前面代码的输出

图- 7 导入节点:这里显示了从Book_continue.xml导入节点并追加到Book.xml后的样子

检查节点的等同性

检查两个节点是否相同使用isSameNode方法。

bool DOMNode::isSameNode(DOMNode $node):当节点是相等的时候,这个函数返回一个布尔值TRUE,否则返回FALSE,$node参数表示你要和当前节点进行比较 的节点。

注意比较不是基于节点的内容进行的:

  1. //检查两个节点是否相同  
  2. $author1 = $root->getElementsByTagName('autor')->item(0);  
  3. $author2 = $root->getElementsByTagName('autor')->item(1);  
  4. //调用verifyNodes函数  
  5. verifyNodes($author1,$author2);  
  6. function verifyNodes($currentNode, $node)  
  7. {  
  8.    if (($currentNode->isSameNode($node))==true)  
  9.    {  
  10.       echo "These two nodes are the same";  
  11.    }     

创建新的树

PHP 5 DOM扩展可以让你从零开始构建DOM树,下面的示例创建了一个全新的XML文档,使用了两个新函数创建注释和CDATA节点。

1、DOMComment DOMDocument::createComment(string $data):创建一个新的注释节点,$data参数表示节点的内容。

2、DOMCDATASection DOMDocument::createCDATASection(string $data):创建一个新的CDATA节点,$data参数表示节点的内容。

  1. <?php   
  2.   //创建一个文档实例   
  3.   $document = new DOMDocument();  
  4.   //使用缩进格式化输出  
  5.   $document->formatOutput = true;  
  6.   //创建一个注释  
  7.   $comment = $document->createComment('Beautiful flowers!!!');  
  8.   $document->appendChild( $comment );   
  9.   //创建<flowers>根元素   
  10.   $root = $document->createElement( 'flowers' );  
  11.   $document->appendChild( $root );  
  12.   //创建<tulips>子节点  
  13.   $tulips = $document->createElement( 'tulips' );  
  14.   //创建<tulips>元素的第一个子节点<bulbs>,并设置其属性  
  15.    $bulbs_1 = $document->createElement( 'bulbs' );     
  16.   $bulbs_1->setAttribute('price','€ 7.65');  
  17.   $bulbs_1->appendChild($document->createTextNode( 'Parrot'));  
  18.   $tulips->appendChild( $bulbs_1 );  
  19.   //创建<tulips>元素的第二个子节点<bulbs>,并设置其属性  
  20.   $bulbs_2 = $document->createElement( 'bulbs' );      
  21.   $bulbs_2->setAttribute('color','magenta');  
  22.   $bulbs_2->appendChild($document->createTextNode( 'Lily flowering' ));  
  23.   $tulips->appendChild( $bulbs_2 );  
  24.   //追加<tulips>节点到根节点后  
  25.   $root->appendChild( $tulips );  
  26.   //创建CDATA小节  
  27.   $cdata = $document->createCDATASection(  
  28.     '<gladiolus><species>Sword Lily</species>'.  
  29.     '<species>Starface</species></gladiolus>');  
  30.   $document->appendChild( $cdata );   
  31.   //保存对象树到Flowers.xml   
  32.   echo $document->saveXML();  
  33.   $document->save('Flowers.xml');  
  34. ?> 
  35. 新的Flower.xml文档内容如下:  
  36. <?xml version="1.0" encoding="ISO-8859-1"?> 
  37. <!--Beautiful flowers!!!--> 
  38. <flowers> 
  39.   <tulips> 
  40.     <bulbs price="€ 7.65">Parrot</bulbs> 
  41.     <bulbs color="magenta">Lily flowering</bulbs> 
  42.   </tulips> 
  43. </flowers> 
  44. <![CDATA[<gladiolus>  
  45.     <species>Sword Lily</species>  
  46.     <species>Starface</species>  
  47.   </gladiolus>  
  48. ]]> 

清单2中的代码创建了一个对象树,并将其保存为Flowers.xml。

清单2 创建一个新的DOM树

本文简单介绍了PHP 5 DOM扩展,并介绍如何使用它操作XML(或HTML)文档,以及如何从零创建一个DOM树。


0 0
原创粉丝点击