2012-12-28 108 views
6

我試圖解析這樣的數據:PHP:解析只XML命名空間

<vin:layout name="Page" xmlns:vin="http://www.example.com/vin"> 
    <header> 
     {someText} 
     <div> 
      <!-- some invalid xml code --> 
      <aas> 
      <nav class="main"> 
       <vin:show section="Menu" /> 
      </nav> 
     </div> 
    </header> 
</vin:layout> 

如何可以解析這樣的數據在PHP?

我試圖DOM,但它不工作,因爲根元素中的XML格式不正確的。我可以告訴解析器,那個沒有vin命名空間的文件就是文本嗎?

+0

你需要一個解決辦法呢。由於[load_invalid_xml](http://stackoverflow.com/questions/2890120/php-processing-invalid-xml)不起作用,我會嘗試一個SGML工具包,或者SimpleHtmlDom(基於字符串/正則表達式的,而不是的libxml)。作爲快速轉義,使用正則表達式對其進行預處理,以將非命名空間標記轉換爲XML文本,其中包含'= preg_replace(「#<((?!/?w +:\ w +)。*?)>#smix」,「< $ 1 >「,$ xml)'。 (這是一個評論,以避免甚至提到它典型的SO flak。) – mario

+0

爲什麼你想解析這個,你想要做什麼? – GreenRover

+0

這是一些tempalte文件。所有名稱空間標籤都代表函數調用。所以我需要用一些其他內容(文本或更多名稱空間標籤)替換名稱空間標籤。但我認爲唯一的辦法就是使用正則表達式... – dkoch

回答

1

我可能會拋出一個排序上Tagsoup解析器。有些東西可以讀取你的格式,除了這些缺陷外,看起來還不錯。沒有什麼文字會停留在對簡單的基於正則表達式的掃描儀的方式上。我只用你得到的四種節點類型叫我的Tagsoup:Starttag,Endtag,Text和Comment。對於標籤,您需要了解其標記名和NamespacePrefix。它只是爲了方便而命名爲類似於XML/HTML,但實際上這都是「自己動手」,所以不要將這些術語延伸到任何標準。

改變每個標籤的使用(開始或結束)不具有名稱空間前綴可能看起來像($string包含了你在你的問題有數據):

$scanner = new TagsoupIterator($string); 

$nsPrefix = 'vin'; 

foreach ($scanner as $node) { 
    $isTag = $node instanceof TagsoupTag; 
    $isOfNs = $isTag && $node->getTagNsPrefix() === $nsPrefix; 
    if ($isTag && !$isOfNs) { 
     $node = strtr($node, ['&' => '&amp;', '<' => '&lt;']); 
    } 
    echo $node; 
} 

輸出:

<vin:layout name="Page" xmlns:vin="http://www.example.com/vin"> 
    &lt;header> 
     {someText} 
     &lt;div> 
      <!-- some invalid xml code --> 
      &lt;aas> 
      &lt;nav class="main"> 
       <vin:show section="Menu" /> 
      &lt;/nav> 
     &lt;/div> 
    &lt;/header> 
</vin:layout> 

的使用來提取裏面的東西一個命名空間的特定標籤可能看起來像:

$scanner = new TagsoupIterator($string); 
$parser = new TagsoupForwardNavigator($scanner); 

$startTagWithNsPrefix = function ($namespace) { 

    return function (TagsoupNode $node) use ($namespace) { 

     /* @var $node TagsoupTag */ 
     return $node->getType() === Tagsoup::NODETYPE_STARTTAG 
      && $node->getTagNsPrefix() === $namespace; 
    }; 
}; 

$start = $parser->nextCondition($startTagWithNsPrefix('vin')); 
$tag = $start->getTagName(); 
$parser->next(); 
echo $html = implode($parser->getUntilEndTag($tag)); 

輸出:

<header> 
    {someText} 
    <div> 
     <!-- some invalid xml code --> 
     <aas> 
     <nav class="main"> 
      <vin:show section="Menu" /> 
     </nav> 
    </div> 
</header> 

接着部分是取代$string的那部分。作爲Tagsoup提供二進制的偏移和長度,這是很容易的(我的快捷方式通過了SimpleXML有點髒):

$xml = substr($string, 0, $start->getEnd()) . substr($string, $parser->getOffset()); 
$doc = new SimpleXMLElement($xml); 
$doc[0] = $html; 
echo $doc->asXML(); 

輸出:

<vin:layout xmlns:vin="http://www.example.com/vin" name="Page"> 
    &lt;header&gt; 
     {someText} 
     &lt;div&gt; 
      &lt;!-- some invalid xml code --&gt; 
      &lt;aas&gt; 
      &lt;nav class="main"&gt; 
       &lt;vin:show section="Menu" /&gt; 
      &lt;/nav&gt; 
     &lt;/div&gt; 
    &lt;/header&gt; 
</vin:layout> 

根據具體需要,這需要改變實現。例如,這不允許將相同的標籤放在彼此中。它不會讓你失望,但它並不能解決問題。不知道你是否有這種情況,如果你需要添加一些打開/關閉計數器,導航器類可以很容易地擴展,甚至提供兩種終端標籤查找方法。

這裏給出的例子是使用Tagsoup你可以在這個要點看:https://gist.github.com/4415105