使用' foreach'解析XML文档。环

时间:2014-12-30 14:28:19

标签: php mysql xml oop xml-parsing

我是PHP,MySQL和XML的新手......并且一直在尝试围绕类,对象,数组和循环。我正在研究从XML文件中提取数据的解析器,然后将其存储到数据库中。在圣诞节假期期间工作的一个有趣且令人愉快的令人沮丧的挑战。

在发布这个问题之前,我已经浏览了PHP5.x文档,W3C并且还搜索了很多关于stackoverflow的内容。

这是代码......

> XML:

<alliancedata>
    <server>
        <name>irrelevant</name>
    </server>

    <alliances>
        <alliance>
            <alliance id="101">Knock Out</alliance>

            <roles>
                <role>
                    <role id="1">irrelevant</role>
                </role>
            </roles>

            <relationships>
                <relationship>
                    <proposedbyalliance id="102" />
                    <acceptedbyalliance id="101" />
                    <relationshiptype id="4">NAP</relationshiptype>
                    <establishedsince>2014-12-27T18:01:34.130</establishedsince>
                </relationship>
                <relationship>
                    <proposedbyalliance id="101" />
                    <acceptedbyalliance id="103" />
                    <relationshiptype id="4">NAP</relationshiptype>
                    <establishedsince>2014-12-27T18:01:34.130</establishedsince>
                </relationship>
                <relationship>
                    <proposedbyalliance id="104" />
                    <acceptedbyalliance id="101" />
                    <relationshiptype id="4">NAP</relationshiptype>
                    <establishedsince>2014-12-27T18:01:34.130</establishedsince>
                </relationship>
            </relationships>
        </alliance>
</alliancedata>

&gt; PHP:

$xml = simplexml_load_file($alliances_xml); // $alliances_xml = path to file

  // die(var_dump($xml));
  // var_dump prints out the entire unparsed xml file.

  foreach ($xml->alliances as $alliances) {

       // Alliance info 
       $alliance_id = mysqli_real_escape_string($dbconnect, $alliances->alliance->alliance['id']);
       $alliance_name = mysqli_real_escape_string($dbconnect,$alliances->alliance->alliance);

       // Diplomacy info
       $proposed_by_alliance_id = mysqli_real_escape_string($dbconnect,$alliances->alliance->relationships->relationship->proposedbyalliance['id']);
       $accepted_by_alliance_id = mysqli_real_escape_string($dbconnect,$alliances->alliance->relationships->relationship->acceptedbyalliance['id']);
       $relationship_type_id = mysqli_real_escape_string($dbconnect,$alliances->alliance->relationships->relationship->relationshiptype['id']);
       $established_date = mysqli_real_escape_string($dbconnect,$alliances->alliance->relationships->relationship->establishedsince);

// this is my attempt to echo every result
echo "Alliance ID: <b>$alliance_id</b> <br/>";
echo "Alliance NAME: <b>$alliance_name</b> <br/>";
echo "Diplomacy Proposed: <b>$proposed_by_alliance_id</b> <br/>";
echo "Diplomacy Accepted: <b>$accepted_by_alliance_id</b> <br/>";
echo "Diplomacy Type: <b>$relationship_type_id</b> <br/>";
echo "Date Accepted: <b>$established_date</b> <br/>";
echo "<hr/>";
}

&gt; intrepter输出:

Alliance ID: 1 
Alliance NAME: Knock Out
Diplomacy Proposed: 102 
Diplomacy Accepted: 101
Diplomacy Type: 4 
Date Accepted: 2011-10-24T05:08:35.830

我不明白为什么在解析第一行数据后循环才会停止。我最好的猜测是,我的代码不会告诉PHP在解析第一个值之后要做什么。

老实说我不知道​​如何用文字解释这个,所以这里是一个视觉表现。

第一行被解释为

--->$alliance_id
--->$alliance_name
--->$proposed_by_alliance_id
--->$accepted_by_alliance_id
--->$relationship_type_id
--->$established_date

然后对于下一个<relationship>子节点发生以下情况......

---> ?? _(no data)_
---> ?? _(no data)_
--->$proposed_by_alliance_id
--->$accepted_by_alliance_id
--->$relationship_type_id
--->$established_date

由于我没有告诉PHP将$alliance_id$alliance_name添加到<relationship>子节点的每次迭代中,解释器只是决定中止foreach操作。 正如我上面提到的,我是PHP和Stackoverflow的新手,我非常感谢您可以分享的任何帮助或智慧。提前谢谢。

2 个答案:

答案 0 :(得分:2)

您写道,在使用SimpleXML调试遍历XML文档的问题时,您遇到了问题。

你遇到的第一个难题是你的foreach只迭代一次:

foreach ($xml->alliances as $alliances) {

你不能接受这个事实。但是,如果我们使用XML,您已经了解了问题并实际查看了XML文档中有多少<alliances>个元素,我们可以看到SimpleXML在这里做了正确的事情:

  • 文档元素中只有一个(1)<alliances>元素。
  • $xml->alliances有一(1)次迭代。
  • $xml->alliances->count()提供int(1)

也可以轻松验证XML的符合性。您的问题示例中的注释死代码表明您使用var_dump来查看XML是否加载。您不必,如果simplexml_load_file未返回false,则文档已加载(如果您选择 falsy :文档未加载或为空)。

因此,如果您想确保文档已加载,只需检查返回值并在出现问题时抛出异常。

要检查 SimpleXMLElement 包含的XML,您也不应该使用var_dump。而是输出XML。由于此时XML可能非常大,例如只需要前256个字节,这通常可以显示良好的图像:

echo substr($xml->alliances->asXML(), 0, 256), "\n";

<alliances>
    <alliance>
    <alliance id="1">Harmless?</alliance>  
    <foundedbyplayerid id="10"/><alliancecapitaltownid id="14646"/>
    <allianceticker>H?</allianceticker>  
    <foundeddatetime>2010-02-25T14:18:07.867</foundeddatetime>  
    <alliancecapitallastmoved>2012-01-19T17:42
 ^^^^^^^^^

这直接表明您正在迭代名为 alliances 的元素,这些元素在文档中只存在一次。这完全符合您所做的观察,即只有一个foreach。

通过这个非常基本的调试,您可以得出以下结论:

  • 据观察,Foreach只迭代一次(1)。
  • 已命令Foreach迭代名为联盟的元素
  • 由于只有一(1)次迭代,因此必须只有一(1)个联盟元素。
  • 计算联盟元素,结果是一个。
  • 因此确认只有一(1)个联盟元素。

显然,你正在迭代错误的元素。

由于这个错误发现的概要相当广泛(只是为了给你一张图片,你可以在这些图片上改进你的代码,但也有错误检查,特别是向你展示你可以从麻烦开始的地方 - 射击),问题仍然存在,为什么你已经无法发现这一点。到目前为止,这里的答案已经指出了这样一个事实,即你正在迭代错误的元素。然而它没有写出来,但在代码中只是有点神秘:

  

[...]将您的for循环从foreach ($xml->alliances->alliance as $alliance) {更改为foreach ($xml->alliance as $alliance) {

     

那就是

     

Source

当然它很弱,因为它只提供代码但不回答你的任何(编程)问题。

找到原因后,让我们逐步治愈

因此,在发现它是错误的元素之后,很容易解决这个问题:迭代正确的元素。

这可以通过对代码应用增量更改来完成。

首先需要选择正确的元素:

foreach ($xml->alliances->alliance as $alliances) {

这将立即使您的代码吐出很多错误,每次迭代都会有很多错误。并且有很多次迭代。所以你已经可以通过这个小小的变化来说,有些东西被有效地改变成了正确的方向:而不是一次迭代,现在还有更多。

但在用新引入的错误和警告解决问题之前,首先要注意刚改变的代码。接下来要将变量$alliances重命名为$alliance(您的编辑器应该通过使用搜索和替换来支持您(通常 CTRL + R )或者提供一个名为&#34的重构命令;重命名变量&#34;(例如,在Phpstorm中 SHIFT + F6 ))。之后该行(以及以下行也改变了但我不会显示它们)看起来像:

 foreach ($xml->alliances->alliance as $alliance) {

但它还没准备好。由于$xml->alliances->alliance有点笨重,让我们将其移出并为此采取更多的说法变量:$alliances

$alliances = $xml->alliances->alliance;
foreach ($alliances as $alliance) {

需要完成的下一步是纠正您所犯的错误。对于一些不明确的原因,我完全不清楚通过mysqli_real_escape_string()传递所有数据。即使您打算稍后将数据传递到数据库,但这仍然是调用该函数的错误位置。首先提取数据,稍后在准备数据库插入操作时调用该函数,该操作是应用程序的不同部分。

我刚刚更换了所有出现的&#34; mysqli_real_escape_string($dbconnect,&#34;用&#34; trim(&#34;所以最后 - 经过适当的缩进 - 代码已经改为:

$alliances = $xml->alliances->alliance;
foreach ($alliances as $alliance) {

    // Alliance info
    $alliance_id   = trim($alliance->alliance->alliance['id']);
    $alliance_name = trim($alliance->alliance->alliance);

    // Diplomacy info
    $proposed_by_alliance_id = trim($alliance->alliance->relationships->relationship->proposedbyalliance['id']);
    $accepted_by_alliance_id = trim($alliance->alliance->relationships->relationship->acceptedbyalliance['id']);
    $relationship_type_id    = trim($alliance->alliance->relationships->relationship->relationshiptype['id']);
    $established_date        = trim($alliance->alliance->relationships->relationship->establishedsince);

感谢更好的命名变量,现在在许多

中非常明显
  

注意:尝试获取非对象的属性

警告来自:$alliance->alliance->的多次调用都是多余的。如果我们记得你最初对错误的元素进行了迭代,那么这就是对应部分:因为你使用了错误的元素,你不得不多次出错,否则根本就无法提取任何数据。想一想这个。这也意味着,你越早验证你的意图实际上是由代码完成的,那么引入的问题就越少。

这里的好处是,通过替换所有&#34; $alliance->alliance->&#34;这很容易解决。使用&#34; $alliance->&#34;:

$alliances = $xml->alliances->alliance;
foreach ($alliances as $alliance) {

    // Alliance info
    $alliance_id   = trim($alliance->alliance['id']);
    $alliance_name = trim($alliance->alliance);

    // Diplomacy info
    $proposed_by_alliance_id = trim($alliance->relationships->relationship->proposedbyalliance['id']);
    $accepted_by_alliance_id = trim($alliance->relationships->relationship->acceptedbyalliance['id']);
    $relationship_type_id    = trim($alliance->relationships->relationship->relationshiptype['id']);
    $established_date        = trim($alliance->relationships->relationship->establishedsince);

现在再次运行代码表明迭代正常,并且从每个联盟元素获取的信息也可以完美地运行。仍然存在错误,因为正如您在问题中已经说过的那样,您不仅对迭代感到疑惑,还对进一步遍历关系感到疑惑:

Alliance ID ......: 1
Alliance NAME ....: Harmless?
Diplomacy Proposed: 454
Diplomacy Accepted: 1
Diplomacy Type ...: 4
Date Accepted  ...: 2011-10-24T05:08:35.830
-------------------------------------------------
[4x Notice: Trying to get property of non-object]
Alliance ID ......: 2
Alliance NAME ....: Danger
Diplomacy Proposed: 
Diplomacy Accepted: 
Diplomacy Type ...: 
Date Accepted  ...: 
-------------------------------------------------
...

错误消息对应以下四行:

$proposed_by_alliance_id = trim($alliance->relationships->relationship->proposedbyalliance['id']);
$accepted_by_alliance_id = trim($alliance->relationships->relationship->acceptedbyalliance['id']);
$relationship_type_id    = trim($alliance->relationships->relationship->relationshiptype['id']);
$established_date        = trim($alliance->relationships->relationship->establishedsince);

这意味着,再次,您需要应用我现在对您的代码的本部分的答案开头所述的故障排除步骤。

到目前为止,这是代码示例:

$xml = simplexml_load_file($alliances_xml); // $alliances_xml = path to file
if (!$xml) {
    throw new UnexpectedValueException(
        sprintf("Unable to load XML or it was empty. Filename given was %s", var_export($alliances_xml, true))
    );
}

$alliances = $xml->alliances->alliance;
// limit to two iterations for debugging
$alliances = new LimitIterator(new IteratorIterator($alliances), 0, 2);

foreach ($alliances as $alliance) {

    // Alliance info
    $alliance_id   = trim($alliance->alliance['id']);
    $alliance_name = trim($alliance->alliance);

    // Diplomacy info

    $proposed_by_alliance_id = trim($alliance->relationships->relationship->proposedbyalliance['id']);
    $accepted_by_alliance_id = trim($alliance->relationships->relationship->acceptedbyalliance['id']);
    $relationship_type_id    = trim($alliance->relationships->relationship->relationshiptype['id']);
    $established_date        = trim($alliance->relationships->relationship->establishedsince);

    // this is my attempt to echo every result
    echo "Alliance ID ......: $alliance_id\n";
    echo "Alliance NAME ....: $alliance_name\n";
    echo "Diplomacy Proposed: $proposed_by_alliance_id\n";
    echo "Diplomacy Accepted: $accepted_by_alliance_id\n";
    echo "Diplomacy Type ...: $relationship_type_id\n";
    echo "Date Accepted  ...: $established_date\n";
    echo "-------------------------------------------------\n";
}

请注意,我使用命令行执行PHP代码,因为它比通过浏览器通过网络服务器快得多。我也不需要编写HTML来获得格式良好的输出。

答案 1 :(得分:0)

我制作了代码的phpfiddle,经过测试,正在运行。

http://phpfiddle.org/main/code/7agg-si3f

您需要删除

<server>
     <name>Epic1</name>
</server>

并将</alliances>添加到最后,因为它报告了无效的xml

之后从foreach ($xml->alliances->alliance as $alliance) {更改for循环 到foreach ($xml->alliance as $alliance) {

那就是