使用 PHP 解码内部的多个 xml 标签

Posted

技术标签:

【中文标题】使用 PHP 解码内部的多个 xml 标签【英文标题】:Decode multiple xml tags inside using PHP 【发布时间】:2013-12-30 20:00:01 【问题描述】:

我正在寻找一种在字符串中解码多个 XML 标记的“智能方式”,我有以下功能:

function b($params) 
    $xmldata = '<?xml version="1.0" encoding="UTF-8" ?><root>' . html_entity_decode($params['data']) . '</root>';
    $lang = ucfirst(strtolower($params['lang']));
    if (simplexml_load_string($xmldata) === FALSE) 
        return $params['data'];
     else 
        $langxmlobj = new SimpleXMLElement($xmldata);

        if ($langxmlobj -> $lang) 
            return $langxmlobj -> $lang;
         else 
            return $params['data'];
        
    

尝试一下

$params['data'] = '<French>Service DNS</French><English>DNS Service</English> - <French>DNS Gratuit</French><English>Free DNS</English>';
$params['lang'] = 'French';
$a = b($params);
print_r($a);

但是输出:

Service DNS

我希望它基本上输出每个标签,所以结果应该是:

Service DNS - DNS Gratuit

拔掉我的头发。任何快速帮助或指示将不胜感激。


编辑:细化需求。

好像我说的不够清楚;让我再举一个例子

如果我有以下字符串作为输入:

The <French>Chat</French><English>Cat</English> is very happy to stay on *** 
because it makes him <French>Heureux</French><English>Happy</English> to know that it 
is the best <French>Endroit</French><English>Place</English> to find good people with
good <French>Réponses</French><English>Answers</English>.

所以如果我用'French'运行函数,它将返回:

The Chat is very happy to stay on *** 
because it makes him Heureux to know that it 
is the best Endroit to find good people with
good Réponses.

还有“英语”:

The Cat is very happy to stay on *** 
because it makes him Happy to know that it 
is the best Place to find good people with
good Answers.

希望现在更清楚了。

【问题讨论】:

你的 php 版本是什么?您的代码为我输出每个标签($a 是 SimpleXMLElement 对象) 【参考方案1】:

基本上,我会先解析出lang部分,比如:

<French>Chat</French><English>Cat</English>

用这个:

"@(<($defLangs)>.*?</\\2>)+@i"

然后用回调解析出正确的lang str。

如果你有 php 5.3+,那么:

function transLang($str, $lang, $defLangs = 'French|English')

    return preg_replace_callback ( "@(<($defLangs)>.*?</\\2>)+@i", 

            function ($matches) use($lang)
            
                preg_match ( "/<$lang>(.*?)<\/$lang>/i", $matches [0], $longSec );

                return $longSec [1];
            , $str );


echo transLang ( $str, 'French' ), "\n", transLang ( $str, 'English' );

如果不是,有点复杂:

class LangHelper


    private $lang;

    function __construct($lang)
    
        $this->lang = $lang;
    

    public function callback($matches)
    
        $lang = $this->lang;

        preg_match ( "/<$lang>(.*?)<\/$lang>/i", $matches [0], $subMatches );

        return $subMatches [1];
    



function transLang($str, $lang, $defLangs = 'French|English')

    $langHelper = new LangHelper ( $lang );

    return preg_replace_callback ( "@(<($defLangs)>.*?</\\2>)+@i", 
            array (
                    $langHelper,
                    'callback' 
            ), $str );


echo transLang ( $str, 'French' ), "\n", transLang ( $str, 'English' );

【讨论】:

【参考方案2】:

如果我理解正确,您想删除所有“语言”标签,但保留所提供语言的内容。

DOM 是一棵节点树。标签是元素节点,文本存储在文本节点中。 Xpath 允许使用表达式选择节点。因此,获取要保留的语言元素的所有子节点,并将它们复制到语言节点之前。然后删除所有语言节点。即使语言元素包含其他元素节点(例如&lt;em&gt;),这也将起作用。

function replaceLanguageTags($fragment, $language) 
  $dom = new DOMDocument();
  $dom->loadXml(
    '<?xml version="1.0" encoding="UTF-8" ?><content>'.$fragment.'</content>'
  );
  // get an xpath object
  $xpath = new DOMXpath($dom);

  // fetch all nodes with the language you like to keep
  $nodes = $xpath->evaluate('//'.$language);
  foreach ($nodes as $node) 
    // copy all the child nodes of just before the found node
    foreach ($node->childNodes as $childNode) 
      $node->parentNode->insertBefore($childNode->cloneNode(TRUE), $node);
    
    // remove the found node
    $node->parentNode->removeChild($node);
  

  // select all language nodes
  $tags = array('English', 'French');
  $nodes = $xpath->evaluate('//'.implode('|//', $tags));
  foreach ($nodes as $node) 
    // remove them
    $node->parentNode->removeChild($node);
  

  $result = '';
  // we do not need the root node, so save all its children
  foreach ($dom->documentElement->childNodes as $node) 
    $result .= $dom->saveXml($node);
  
  return $result;


$xml = <<<'XML'
The <French>Chat</French><English>Cat</English> is very happy to stay on ***
because it makes him <French>Heureux</French><English>Happy</English> to know that it
is the best <French>Endroit</French><English>Place</English> to find good people with
good <French>Réponses</French><English>Answers</English>.
XML;

var_dump(replaceLanguageTags($xml, 'English'));
var_dump(replaceLanguageTags($xml, 'French'));

输出:

string(146) "The Cat is very happy to stay on ***
because it makes him Happy to know that it
is the best Place to find good people with
good Answers."
string(153) "The Chat is very happy to stay on ***
because it makes him Heureux to know that it
is the best Endroit to find good people with
good Réponses."

【讨论】:

【参考方案3】:

您使用的是哪个版本的 PHP?我不知道还有什么不同,但我复制并粘贴了您的代码并得到以下输出:

SimpleXMLElement Object
(
    [0] => Service DNS
    [1] => DNS Gratuit
)

为了确定,这是我从上面复制的代码:

<?php

function b($params) 
    $xmldata = '<?xml version="1.0" encoding="UTF-8" ?><root>' . html_entity_decode($params['data']) . '</root>';
    $lang = ucfirst(strtolower($params['lang']));
    if (simplexml_load_string($xmldata) === FALSE) 
        return $params['data'];
     else 
        $langxmlobj = new SimpleXMLElement($xmldata);

        if ($langxmlobj -> $lang) 
            return $langxmlobj -> $lang;
         else 
            return $params['data'];
        
    


$params['data'] = '<French>Service DNS</French><English>DNS Service</English> - <French>DNS Gratuit</French><English>Free DNS</English>';
$params['lang'] = 'French';
$a = b($params);
print_r($a);

【讨论】:

不,这不是更清楚。使用您的新字符串,您的代码会生成 SimpleXMLElement 对象( [0] => Chat [1] => Heureux [2] => Endroit [3] => Reponses )也许您需要 print_r 以外的函数,但不清楚您是什么正在努力实现或您目前的结果是什么。如果你想在你的问题中输出一个段落,不要使用 print_r,这样做: $a[0] 很高兴留在 *** 上,因为它让他 $a[1] 知道它是最好的 $a[2] 用 $a[3] 找到好人。 没有。我希望函数返回用正确语言翻译的文本;我不想通过数组和索引。请忽略最后的'print_r'函数; print $a 应该打印翻译后的文本。 据我所知,除了通过数组之外,没有简单的方法可以用 PHP 做你想做的事情。【参考方案4】:

这是我的建议。它应该很快而且很简单。您只需要剥离所需语言的标签,然后删除任何其他标签及其内容。

不利的一面是,如果您希望使用语言标签以外的任何其他标签,则必须确保开始标签与结束标签不同(例如,&lt;p &gt;Lorem&lt;/p&gt; 而不是&lt;p&gt;Lorem&lt;/p&gt;)。另一方面,这允许您添加任意数量的语言,而无需保留它们的列表。当要求的语言丢失时,您只需要知道默认的(或只是抛出并捕获异常)。

function only_lang($lang, $text) 
    static $infinite_loop;

    $result = str_replace("<$lang>", '', $text, $num_matches_open);
    $result = str_replace("</$lang>", '', $result, $num_matches_close);

    // Check if the text is malformed. Good place to throw an error
    if($num_matches_open != $num_matches_close) 
        //throw new Exception('Opening and closing tags does not match', 1);

        return $text;
    

    // Check if this language is present at all.
    // Otherwise fallback to default language or throw an error
    if( ! $num_matches_open) 
        //throw new Exception('No such language', 2);

        // Prevent infinite loop if even the default language is missing
        if($infinite_loop) return $text;
        $infinite_loop = __FUNCTION__;
        return $infinite_loop('English', $text);
    

    // Strip any other language and return the result
    return preg_replace('!<([^>]+)>.*</\\1>!', '', $result);

【讨论】:

【参考方案5】:

我用正则表达式得到了一个简单的。如果输入仅包含 &lt;lang&gt;...&lt;/lang&gt; 标记,则很有用。

function to_lang($lang="", $str="") 
  return strip_tags(preg_replace('~<(\w+(?<!'.$lang.'))>.*</\1>~Us',"",$str));


echo to_lang("English","The happy <French>Chat</French><English>Cat</English>");

删除每个&lt;tag&gt;...&lt;/tag&gt;,这不是$lang 中指定的一个。如果&lt;tag-name&gt; 中可能有空格/特价,例如&lt;French-1&gt;\w 替换为 [^/&gt;]


解释一下搜索模式

1.) &lt;(\w+(?&lt;!'.$lang.'))

&lt; 后跟一个或多个Word characters, 不匹配$lang(使用negative lookbehind) 并捕获&lt;tag_name&gt;

2.) .* 后跟任何内容(不贪婪:modifier U点匹配换行符:修饰符 s )

3.) &lt;/\1&gt; 直到捕获的标签被关闭

【讨论】:

以上是关于使用 PHP 解码内部的多个 xml 标签的主要内容,如果未能解决你的问题,请参考以下文章

如何将数组内部指针设置到特定位置? PHP/XML

对象内部套嵌多个对象

ng-style 内部的多个函数

如何在方向更改时调整多个标签的大小以自动适应 UIView 内部

Django 模板的 Vim 内部标签块

如何处理在 php mysql 中返回多个值的内部查询?