PHP / SimpleXML - 为单个子项和多个子项生成不同的数组

时间:2014-05-20 17:17:41

标签: php xml simplexml

我正在使用SimpleXML来解析来自不同房地产经纪人的房产列表的XML Feed。 XML提要的相关部分如下所示:

<branch name="Trustee Realtors">
    <properties>
        <property>
            <reference>1</reference>
            <price>275000</price>
            <bedrooms>3</bedrooms>
        </property>
        <property>
            <reference>2</reference>
            <price>350000</price>
            <bedrooms>4</bedrooms>
        </property>
        <property>
            <reference>3</reference>
            <price>128500</price>
            <bedrooms>4</bedrooms>
        </property>
    </properties>
</branch>
<branch name="Quick-E-Realty Inc">
    <properties>
        <property>
            <reference>4</reference>
            <price>180995</price>
            <bedrooms>3</bedrooms>
        </property>
    </properties>
</branch>

然后转换为这样的数组:

$xml = file_get_contents($filename);
$xml = simplexml_load_string($xml);
$xml_array = json_decode(json_encode((array) $xml), 1);
$xml_array = array($xml->getName() => $xml_array);

我遇到的问题是,在创建数组时,单个列表的数据在数组中与多个列表的位置不同 - 我不确定如何解释这个,但是如果我var_dump ()看起来像这样的多个项目的数组:

array(3) {
    [0]=>
    array(3) {
        ["reference"]=>
        string(4) "0001"
        ["price"]=>
        string(6) "275000"
        ["bedrooms"]=>
        int(3)
    }
    [1]=>
    array(3) {
        ["reference"]=>
        string(4) "0002"
        ["price"]=>
        string(6) "350000"
        ["bedrooms"]=>
        int(4)
    }
    [2]=>
    array(3) {
    ["reference"]=>
        string(4) "0003"
        ["price"]=>
        string(6) "128500"
        ["bedrooms"]=>
        int(2)
    }
}

如果我var_dump()单个列表的数组,它看起来像这样:

array(3) {
    ["reference"]=>
    string(4) "0004"
    ["price"]=>
    string(6) "180995"
    ["bedrooms"]=>
    int(3)
}

但我需要它看起来像这样:

array(1) {
    [0]=>
    array(3) {
        ["reference"]=>
        string(4) "0004"
        ["price"]=>
        string(6) "180995"
        ["bedrooms"]=>
        int(3)
    }
}

这些数组中的每一个都代表一个房地产经纪人的房产清单。我不确定这是否只是SimpleXML或json函数的工作方式,但我需要的是使用相同的格式(包含属性列表的数组是[0]键的值)。 / p>

提前致谢!

8 个答案:

答案 0 :(得分:4)

SimpleXML像这样古怪。我最近使用它试图制作配置文件&#34;更容易&#34;写下来并发现SimpleXML并不始终保持一致。在这种情况下,我认为您将受益于简单地检测<property>是否是集合中的唯一一个,如果是,则将其自身包装在一个数组中,然后将其发送到您的循环。

注意:['root']是因为我需要在XML周围包含'<root></root>'元素以使我的测试工作。

//Rebuild the properties listings
$rebuild = array();
foreach($xml_array['root']['branch'] as $key => $branch) {
    $branchName = $branch['@attributes']['name'];
    //Check to see if 'properties' is only one, if it
    //is then wrap it in an array of its own.
    if(is_array($branch['properties']['property']) && !isset($branch['properties']['property'][0])) {
        //Only one propery found, wrap it in an array
        $rebuild[$branchName] = array($branch['properties']['property']);
    } else {
        //Multiple properties found
        $rebuild[$branchName] = $branch['properties']['property'];
    }
}

负责重建您的房产。感觉有点hackish。但基本上你在这里检测到缺少多维数组:

if(is_array($branch['properties']['property']) && !isset($branch['properties']['property'][0]))

如果您没有找到多维数组,那么您明确地制作单个<property>之一。然后,为了测试所有内容都已正确重建,您可以使用以下代码:

//Now do your operation...whatever it is.
foreach($rebuild as $branch => $properties) {
    print("Listings for $branch:\n");
    foreach($properties as $property) {
        print("Reference of " . $property['reference'] . " sells at $" . $property['price'] . " for " . $property['bedrooms'] . " bedrooms.\n");
    }
    print("\n");
}

这会产生以下输出:

Listings for Trustee Realtors:
Reference of 1 sells at $275000 for 3 bedrooms.
Reference of 2 sells at $350000 for 4 bedrooms.
Reference of 3 sells at $128500 for 4 bedrooms.

Listings for Quick-E-Realty Inc:
Reference of 4 sells at $180995 for 3 bedrooms.

重建的转储将产生:

Array
(
    [Trustee Realtors] => Array
        (
            [0] => Array
                (
                    [reference] => 1
                    [price] => 275000
                    [bedrooms] => 3
                )

            [1] => Array
                (
                    [reference] => 2
                    [price] => 350000
                    [bedrooms] => 4
                )

            [2] => Array
                (
                    [reference] => 3
                    [price] => 128500
                    [bedrooms] => 4
                )

        )

    [Quick-E-Realty Inc] => Array
        (
            [0] => Array
                (
                    [reference] => 4
                    [price] => 180995
                    [bedrooms] => 3
                )

        )

)

我希望这可以帮助您更接近解决问题的方法。

答案 1 :(得分:4)

巨大的&#34;在盒子外思考&#34;这里要问自己的问题是:为什么首先将SimpleXML对象转换为数组?

SimpleXML不仅仅是一个用于解析XML然后使用其他东西来操作它的库,它专为完全设计的那种你将要做的事情。阵列。

事实上,有时候单个元素有时具有多个元素的问题是它比普通数组表示的一大优势:对于你知道的单个节点,你可以不用[0];但对于您知道的节点可能是多个节点,您可以使用[0]foreach循环,也可以使用

以下是一些使用XML实现SimpleXML名称的原因:

$sxml = simplexml_load_string($xml);

// Looping over multiple nodes with the same name
// We could also use $sxml->children() to loop regardless of name
//   or even the shorthand foreach ( $sxml as $children )
foreach ( $sxml->branch as $branch ) {

    // Access an attribute using array index notation
    //   the (string) is optional here, but good habit to avoid
    //   passing around SimpleXML objects by mistake
    echo 'The branch name is: ' . (string)$branch['name'] . "\n";

    // We know there is only one <properties> node, so we can take a shortcut:
    //   $branch->properties means the same as $branch->properties[0]
    // We don't know if there are 1 or many <property> nodes, but it
    //   doesn't matter: we're asking to loop over them, so SimpleXML 
    //   knows what we mean
    foreach ( $branch->properties->property as $property ) {
        echo 'The property reference is ' . (string)$property->reference . "\n";
    }
}

基本上,只要我看到那个丑陋的json_decode(json_encode(技巧,我就会畏缩一点,因为接下来的代码中有99次比使用SimpleXML更加丑陋。

答案 2 :(得分:2)

一种可能性是使用DOM + XPath读取XML。 XML不能只转换为JSON,但为特定的XML构建特定的JSON很容易:

$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXPath($dom);

$result = [];
foreach ($xpath->evaluate('//branch') as $branchNode) {
  $properties = [];
  foreach ($xpath->evaluate('properties/property', $branchNode) as $propertyNode) {
    $properties[] = [
      'reference' => $xpath->evaluate('string(reference)', $propertyNode),
      'price' => (int)$xpath->evaluate('string(price)', $propertyNode),
      'bedrooms' => (int)$xpath->evaluate('string(bedrooms)', $propertyNode)
    ];
  }
  $result[] = [
    'name' => $xpath->evaluate('string(@name)', $branchNode),
    'properties' => $properties
  ];
}

echo json_encode($result, JSON_PRETTY_PRINT);

输出:https://eval.in/154352

[
    {
        "name": "Trustee Realtors",
        "properties": [
            {
                "reference": "1",
                "price": 275000,
                "bedrooms": 3
            },
            {
                "reference": "2",
                "price": 350000,
                "bedrooms": 4
            },
            {
                "reference": "3",
                "price": 128500,
                "bedrooms": 4
            }
        ]
    },
    {
        "name": "Quick-E-Realty Inc",
        "properties": [
            {
                "reference": "4",
                "price": 180995,
                "bedrooms": 3
            }
        ]
    }

答案 3 :(得分:1)

使用SimpleXMLElement Class

 <?php
 $xml = "<body>
 <item>
 <id>2</id>
 </item>
 </body>";
$elem  =  new SimpleXMLElement($xml);
 if($elem->children()->count() === 1){
    $id = $elem->item->addChild(0)->addChild('id',$elem->item->id);
    unset($elem->item->id);
 };

$array =  json_decode(json_encode($elem), true);
print_r($array);

输出:

  Array
  (
    [item] => Array
    (
        [0] => Array
            (
                [id] => 2
            )

    )

  )

答案 4 :(得分:0)

你使用过这个:

$xml_array['branch']['properties']['property']

作为循环源?试着用这个:

$xml_array['branch']['properties']

不要在行尾使用['property'],不要使用3段只使用2段

<?php
$xml = file_get_contents('simple.xml');
$xml = simplexml_load_string($xml);
$xml_array = json_decode(json_encode((array) $xml), 1);
$xml_array = array($xml->getName() => $xml_array);
print_r($xml_array);
foreach($xml_array['branch']['properties'] as $a){
    print_r($a);
}
?>

答案 5 :(得分:0)

为了解决这个问题,你应该选择使用xpath(如其他提及的那样),但在我看来,对于大多数网络开发者来说,这不是一个非常熟悉的工具。我创建了一个非常小的作曲家启用包,它解决了这个问题。感谢symfony包CssSelector(https://symfony.com/doc/current/components/css_selector.html),它将CSS选择器重写为xpath选择器。我的软件包只是一个瘦的包装器,它实际上处理了你在最常见的情况下使用PHP对XML做什么。您可以在此处找到它:https://github.com/diversen/simple-query-selector

use diversen\querySelector;

// Load simple XML document
$xml = simplexml_load_file('test2.xml');


// Get all branches as DOM elements 
$elems = querySelector::getElementsAsDOM($xml, 'branch');

foreach($elems as $elem) {
    // Get attribute name
    echo $elem->attributes()->name . "\n";
    // Get properties as array
    $props = querySelector::getElementsAsAry($elem, 'property');
    print_r($props); // You will get the array structure you expect
}

你也可以(如果你不关心分支名称)只做:

$elems = querySelector::getElementsAsAry($xml, 'property');

答案 6 :(得分:0)

测试解析后的XML是否有多个标签,或者是单个标签转换为数组,而不是重建数组,您只需测试以下情况:

<?xml version="1.0" encoding="ISO-8859-1" ?>

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
         version="2.4">

</web-app>

答案 7 :(得分:0)

试试吧=)

$xml = simplexml_load_string($xml_raw, "SimpleXMLElement", LIBXML_NOCDATA);
$json = json_encode($xml);
$array = json_decode($json, TRUE);
$marray['RepairSheets']['RepairSheet'][0] = $array['RepairSheets']['RepairSheet'];
$array = (isset($array['RepairSheets']['RepairSheet'][0]) == true) ? $array : $marray;