doumu6941 2015-03-08 17:00
浏览 61
已采纳

使用php迭代XML文件

I have an XML file with the following:

<property>
  <id>1</id>
  <type>type</type>
  <town>town</town>
  <province>province</province>
  <images>
    <image id="1">
    <url>
      http://www.test.com
    </url>
    <image id="2">
    <url>
      http://www.test.com
    </url>
    <image id="3">
    <url>
      http://www.test.com
    </url>
  </image>

I can iterate through the file and get the value except the image url. I am struggling with the element after the element with an attribute.

$count=0;
$id=0;
foreach($xml->children() as $properties) {
    echo "<h1>" . $xml->property[$count]->type . " for sale in " .$xml->property[$count]->town . ", " . $xml->property[$count]->province . "</h1>" . "<br>";
    echo $xml->property[$count]->id . "<br>";
    echo $xml->property[$count]->desc->en . "<br>";

    foreach($xml->property[$count]->children() as $images) {
        echo $xml->property[$count]->images -> image[$id++] -> url;
        $id++;
}
    $count++;
}

but the 2nd loop isn´t close to being right. I would greatly appreciate some help.

  • 写回答

2条回答 默认 最新

  • dongzhang6677 2015-03-08 20:09
    关注

    You have not shared the complete XML structure so it's unclear where the root of the document is (the document element).

    Assuming that the root element is not <property> but all <property> elements are children of the root element you can iterate over all those <property> elements by simply just iterating over them:

    $xml = simplexml_load_file('example.xml');
    
    foreach ($xml->property as $property) {
        printf(
            "<h1>%s for sale in %s, %s</h1>
    ", htmlspecialchars($property->id), 
            htmlspecialchars($property->town), htmlspecialchars($property->province)
        );
    

    As you can see, you don't need to make use of a $count variable explicitly. You can, but you don't need to. Just saying.

    Now you're looking for the image URLs. Each <property> element has a single child element named <images> and these again have mulitple <url> elements you're interested in.

    This can be done with an xpath query (as you go down deeper more than one level):

    $urls = $property->xpath('images/image/url');
    foreach ($urls as $url) {
        printf(" - %s
    ", htmlspecialchars(trim($url)));
    }
    

    If you don't use xpath here, you would have needed to make a foreach for every single level that contains more than a single child. Just as a counter-example:

    foreach ($property->images->image as $image) {
        foreach ($image->url as $url) {
            printf(" - %s
    ", htmlspecialchars(trim($url)));
        }
    }
    

    I hope this shows you more well how the traversal works in simplexml. There is some more great material in the PHP manual entitled Basic SimpleXML usage which shows basic traversal and also links to the more advanced xpath topic.

    And don't forget when you output dynamic data into HTML to properly HTML encode it. I've used the htmlspecialchars function for that, you need to provide the correct encoding parameters which I've left out for brevity in my answer.

    Example in full:

    <?php
    /**
     * @link https://stackoverflow.com/questions/28929239/iterating-through-xml-file-with-php
     */
    
    $buffer = <<<XML
    <root>
        <property>
            <id>1</id>
            <type>type</type>
            <town>town</town>
            <province>province</province>
            <images>
                <image id="1">
                    <url>
                        http://www.test.com
                    </url>
                </image>
                <image id="2">
                    <url>
                        http://www.test.com
                    </url>
                </image>
                <image id="3">
                    <url>
                        http://www.test.com
                    </url>
                </image>
            </images>
        </property>
        <property>
            <id>1</id>
            <images>
                <image id="1">
                    <url>
                        http://www.test.com
                    </url>
                </image>
             </images>
        </property>
    </root>
    XML;
    
    
    $xml = simplexml_load_string($buffer);
    
    foreach ($xml->property as $property) {
        printf(
            "<h1>%s for sale in %s, %s</h1>
    ",
            htmlspecialchars($property->id), htmlspecialchars($property->town), htmlspecialchars($property->province)
        );
    
        $urls = $property->xpath('images/image/url');
        foreach ($urls as $url) {
            printf(" - %s
    ", htmlspecialchars(trim($url)));
        }
    }
    

    Exemplary output (plain-text):

    <h1>1 for sale in town, province</h1>
     - http://www.test.com
     - http://www.test.com
     - http://www.test.com
    <h1>1 for sale in , </h1>
     - http://www.test.com
    

    Transliteration and Output Encoding with SimpleXML

    One way to globally apply transliteration (removal of accents) and HTML encoding (htmlspecialchars) for every string-value of the SimpleXMLElement can be achieved by extending it. There is a shortcomming: a SimpleXMLElement can't have private properties (because the properties are all magic), however you can create static global variables. For keeping the instance of a Transliterator this is enough. And for manipulating the string values, the __toString() magic method works well with SimpleXMLElement:

    /**
     * Transliterate and HTML encode
     *
     * Class XMLTransliterated
     */
    class XMLTransliterated extends SimpleXMLElement
    {
        public static $transliterator;
    
        public function __toString()
        {
            $transliterator = &self::$transliterator;
            $transliterator || $transliterator = Transliterator::create("Latin-ASCII");
    
            $transliterated = $transliterator->transliterate($this);
    
            return htmlspecialchars(trim($transliterated), ENT_QUOTES | ENT_HTML5, 'UTF-8');
        }
    }
    

    All you need to do to benefit from the string manipulations is to either use this classname XMLTransliterated (or whichever you would name the class) when creating the SimpleXMLElement:

    $xml = simplexml_load_string($buffer, 'XMLTransliterated');
    

    - or - and this is very special to simplexml, you can change the class later on with a little conversion trick:

    $xml = simplexml_import_dom(dom_import_simplexml($xml), 'XMLTransliterated');
    

    This will make the following code use an XMLTransliterated instead of the previous less specific SimpleXMLElement so the code largely remains the same (please note that the htmlspecialchars and trim calls could be safely removed as they are now automatically called when accessing the string-values of the SimpleXMLElement):

    $xml = simplexml_load_string($buffer, 'XMLTransliterated');
    
    foreach ($xml->property as $property) {
        printf(
            "<h1>%s (%s) for sale in %s, %s</h1>
    ",
            $property->id, $property->type, $property->town, $property->province
        );
    
        $urls = $property->xpath('images/image/url');
        foreach ($urls as $url) {
            printf(" - %s
    ", $url);
        }
    }
    

    But the output will turn "Schloß" into "schloss", "tôwn" into "town" and "provincé" into "province".

    Transliterator requires PHP 5.4 and you having the Intl extension enabled (which you should have, if not enable it).

    Alternatively you can also make use of transliteration from the iconv library. But beware, this produces slightly different output:

    $transliterated = iconv('UTF-8', 'ASCII//IGNORE//TRANSLIT', $this);
    

    More related transliteration questions:

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 不小心不正规的开发公司导致不给我们y码,
  • ¥15 我的代码无法在vc++中运行呀,错误很多
  • ¥50 求一个win系统下运行的可自动抓取arm64架构deb安装包和其依赖包的软件。
  • ¥60 fail to initialize keyboard hotkeys through kernel.0000000000
  • ¥30 ppOCRLabel导出识别结果失败
  • ¥15 Centos7 / PETGEM
  • ¥15 csmar数据进行spss描述性统计分析
  • ¥15 各位请问平行检验趋势图这样要怎么调整?说标准差差异太大了
  • ¥15 delphi webbrowser组件网页下拉菜单自动选择问题
  • ¥15 wpf界面一直接收PLC给过来的信号,导致UI界面操作起来会卡顿