【问题标题】:PHP & XML: How do I compare the text content of two XML Elements?PHP & XML:如何比较两个 XML 元素的文本内容?
【发布时间】:2018-10-25 14:07:18
【问题描述】:

我正在尝试编写一个脚本,该脚本遍历三个现有 XML 文档并编译第四个 XML 文档,该文档包含现有三个中的所有语素(语言学家对部分单词的说法)。我正在尝试确保这个新的词素数据库不包含任何重复项,并且我无法让它不添加重复项。我将在下面发布相关的 sn-p,并在底部发布整个相关代码。

重复检查如下:((string)$source == (string)$storySource),其中 $source 和 $storySource 都是 simpleXMLElement,如下所示:<m>text</m>。谁能告诉我哪里出错了?

最好, 吉米

这是遍历其中一个 XML 文件的整个循环。

$storycorpus = new SimpleXMLElement($file,null,true);
$storyEntries = $storycorpus->xpath("//morpheme");
foreach($storyEntries as $entry){
    // check to see if in morpheme database. we will match the Pomo and the English, hence, if either is not a match,
    // we will add a new morpheme
    $storySource = $entry->m;
    $storyGloss = $entry->g;
    // set a variable equal to false
    $foundInDB = false; 

    //we will loop through the database looking for a match.    
    foreach($morphemeEntries as $existingMorpheme){
        $source = $existingMorpheme->source;
        $gloss = $existingMorpheme->gloss;

        // if we find a match, we will set our variable to be true and break out of the morpheme DB loop
        if(((string)$source == (string)$storySource) && ((string)$gloss == (string)$storyGloss)){
            $foundInDB = true; // problem: this line isn't firing
            break;
        }
    }
    // after the morphemeDB loop, we will check to see if the var is true. 
    if($foundInDB == true){
        // if it is true, we don't need to enter anything and can 
        // go to the next entry
        continue;
    } else{
        // if we didn't find a match, create a new morpheme
        $newMorphemeEntry = $morphemeDB->addChild("morpheme");
        $newMorphemeEntry->addChild("source", $storySource);
        $newMorphemeEntry->addChild("gloss", $storyGloss);
        $newMorphemeEntry->addChild("root", $storySource);
        $newMorphemeEntry->addChild("hypernym", $storySource);
        $newMorphemeEntry->addChild("link", "S");
        if(substr($storySource, 0, 1) == "-"){
            $newMorphemeEntry->addChild("affix", "suffix");
        } elseif(substr($storySource, -1, 1) == "-"){
            $newMorphemeEntry->addChild("affix", "prefix");
        } else{
            $newMorphemeEntry->addChild("affix", "root");
        }
    }
}

好的,所以我重写了该块并使用 DOMDocument 而不是 SimpleXML,但在防止重复方面我仍然没有任何运气。这是新代码

    // check to see if in morpheme database. we will match the Pomo and the English, hence, if either is not a match,
    // we will add a new morpheme
    $phraseSource = $entry->nodeValue;
    $phraseGlossId = $entry->getAttribute("id");
    $phraseGloss = $xpath2->query("//g[@id =\"$phraseGlossId\"]")->item(0)->nodeValue;
    // set a variable equal to false
    $foundInDB = false; 

    //we will loop through the database looking for a match.    
    foreach($morphemeEntries as $existingMorpheme){
        $source = $existingMorpheme->getElementsByTagName("source")->item(0)->nodeValue;
        $gloss = $existingMorpheme->getElementsByTagName("gloss")->item(0)->nodeValue;
        // if we find a match, we will set our variable to be true and break out of the morpheme DB loop
        if(($source == $phraseSource) && ($gloss == $phraseGloss)){
            $foundInDB = true; // problem: this line isn't firing
            break;
        }
    }
    // after the morphemeDB loop, we will check to see if the var is true. 
    if($foundInDB == true){
        // if it is true, we don't need to enter anything and can 
        // go to the next entry
        continue;
    } else{
        // if we didn't find a match, create a new morpheme
        $newMorphemeEntry = $morphemeXmlDoc->createElement("morpheme");

        $newMorphemeSource = $morphemeXmlDoc->createElement("source");
        $newMorphemeSource->nodeValue = $phraseSource;
        $newMorphemeEntry->appendChild($newMorphemeSource);

        $newMorphemeGloss = $morphemeXmlDoc->createElement("gloss");
        $newMorphemeGloss->nodeValue = $phraseGloss;
        $newMorphemeEntry->appendChild($newMorphemeGloss);

        $newMorphemeRoot = $morphemeXmlDoc->createElement("root");
        $newMorphemeRoot->nodeValue = $phraseSource;
        $newMorphemeEntry->appendChild($newMorphemeRoot);

        $newMorphemeHypernym = $morphemeXmlDoc->createElement("hypernym");
        $newMorphemeHypernym->nodeValue = $phraseSource;
        $newMorphemeEntry->appendChild($newMorphemeHypernym);

        $newMorphemeLink = $morphemeXmlDoc->createElement("link");
        $newMorphemeLink->nodeValue = "P";
        $newMorphemeEntry->appendChild($newMorphemeLink);

        $newMorphemeAffix = $morphemeXmlDoc->createElement("affix");
        $newMorphemeAffix->nodeValue = $phraseGloss;

        if(substr($phraseSource, 0, 1) == "-"){
            $newMorphemeAffix->nodeValue = "suffix";
        } elseif(substr($phraseSource, -1, 1) == "-"){
            $newMorphemeAffix->nodeValue = "prefix";
        } else{
            $newMorphemeAffix->nodeValue = "root";
        }
        $newMorphemeEntry->appendChild($newMorphemeAffix);

        $morphemeRootNode->appendChild($newMorphemeEntry);
    }
}

这是脚本正在搜索的内容以创建新的 XML 表:

<phrasicon>
<phrase id="4">
    <ref1>ES</ref1>
    <source>t̪o: xa jo: k'ala:</source>
    <morpheme>
      <m id="4.1">t̪o:</m>
      <m id="4.2">xa</m>
      <m id="4.3">jo:</m>
      <m id="4.4">k'ala:</m>
    </morpheme>
    <gloss lang="en">
      <g id="4.1">me</g>
      <g id="4.2">water</g>
      <g id="4.3">for</g>
      <g id="4.4">die</g>
    </gloss>
    <translation lang="en">I'm dying for water.</translation>
    <media1 mimeType="audio/wav" url="im_dying_for_water.wav"/>
    <ref2/>
    <media2 mimeType="" url=""/>
    <ref3/>
    <media3 mimeType="" url=""/>
  </phrase>
</phrasicon>

这是新的词素 XML 表的外观

<?xml version="1.0" encoding="UTF-8"?>
<morphemedatabase>
<morpheme>
  <source>t̪o:</source>
  <gloss>me</gloss>
  <root>t̪o:</root>
  <hypernym>t̪o:</hypernym>
  <link>P</link>
  <affix>root</affix>
</morpheme>
</morphemedatabase>

【问题讨论】:

  • 我强烈建议您停止使用简单的 xml,而开始使用 DOMDocument。更好地合作
  • 您好 Delboy,感谢您的反馈!这个项目已经有很多使用简单 xml 的 PHP 代码,所以对于我和我一起工作的其他开发人员来说,在某些地方使用 DOMDocument 而在其他地方使用简单的 xml 会带来麻烦。那,我发现 DOMDocument 对于这些东西有点笨拙。话虽如此,我将尝试在这里使用 DOMDocument 看看会发生什么。再次感谢!
  • 祝你好运,如果您遇到 DOM 类问题,请更新您的问题,我们会尽力提供帮助!
  • 谢谢,非常感谢!作为未来的一般原则,您认为值得我们花时间重写我们所有使用简单 xml 的代码吗?还是我们应该从现在开始使用 DOMDocument 编写所有内容并保留我们的 simplexml 代码,直到我们将来出于其他原因必须对其进行编辑?
  • 如果它有效,不,只有当你真的需要时!对于新的东西,绝对!

标签: php xml xml-parsing


【解决方案1】:

我想$morphemeEntriesSimpleXMLElement 对象的固定列表,不会随着添加的节点而更新。我建议使用$morphemeDB 对象进行检查。此外,您可以用 Xpath 表达式替换循环。

$storySource = $entry->m;
$storyGloss = $entry->g;

$foundInDB = count(
  $morphemeDB->xpath(
    sprintf('.//morpheme[source="%s" and gloss="%s"]', $storySource, $storyGloss)
  )
) > 0; 

在 DOM 中,DOMXpath::evaluate() 也是如此:

$phraseSource = $xpathSource->evaluate('string(m)', $entry);
$phraseGloss = $xpathSource->evaluate('string(g)', $entry);

$foundInDB = $xpathTarget->evaluate(
  sprintf(
    'count(//morpheme[source="%s" and gloss="%s"]) > 0', 
    $storySource, 
    $storyGloss
  )
);

在 DOM 实现中,您可以将 createElement() 嵌套到 appendChild() 中,但您应该将内容添加为文本节点(为了正确转义):

$newMorphemeEntry = $morphemeRootNode->appendChild(
  $morphemeXmlDoc->createElement("morpheme")
);
$newMorphemeEntry
  ->appendChild($morphemeXmlDoc->createElement("source"))
  ->appendChild($morphemeXmlDoc->createTextNode($phraseSource));
$newMorphemeEntry
  ->appendChild($morphemeXmlDoc->createElement("gloss"))
  ->appendChild($morphemeXmlDoc->createTextNode($phraseGloss));

【讨论】:

    【解决方案2】:

    在比较之前不要尝试转换为(字符串)。 而是在每个元素上调用 -&gt;asXML() 方法。 替换这个:

    if(((string)$source == (string)$storySource) && ((string)$gloss == (string)$storyGloss))
    

    用这个:

    if(($source->asXML() == $storySource->asXML()) && ($gloss->asXML() == $storyGloss->asXML()))
    

    或比较包含的字符串(不包括标签)

    if(($source->__toString() == $storySource->__toString()) && ($gloss->__toString() == $storyGloss->__toString()))
    

    问题在于 SimpleXMLElement 不是“经典”PHP 对象。 SimpleXML 是使用链接到 XML 文档的内部表示的“实时”API 构建的。

    Comparing Objects 上的手册页说“如果两个对象实例具有相同的属性和值,并且是同一类的实例,则它们是相等的。”

    在 SimpleXMLElement 上的 print_r() 或 var_dump() 中显示为表示子节点和属性的属性。但是,实际的实现只包含一个指向在解析 XML 时创建的内存结构的指针,即使您将相同的字符串解析两次,它也会有所不同。因此,简单地将两个 SimpleXMLElement 对象与 == 进行比较将永远不会返回 true。

    【讨论】:

    • 感谢您的回答!不会 ->asXML() 比较整个 XML 元素,包括标签?我的问题是我正在尝试比较两个可能看起来像 text1 和 text2 的元素的文本内容。因此,比较 XML 对我没有帮助。虽然,在使用 ->asXML() 之后,我可能会想出一种方法来修剪标签。
    • 你可以使用->__toString()
    • 我可以,但是我必须想出一种方法来从字符串中删除 XML 标记。我将采纳delboy1978uk 的建议并使用DOMDocument 而不是SimpleXML 重写它。无论如何,mille grazie,da un italiano ad un altro!
    猜你喜欢
    • 2022-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多