【问题标题】:Create PHP Class or Object from XML Document从 XML 文档创建 PHP 类或对象
【发布时间】:2015-03-23 20:55:49
【问题描述】:

我正在处理由 API 返回的 XML 文档。 XML 返回一个产品列表,其中包含每个项目的属性,例如库存、项目编号、名称、价格等。

我可以遍历所有 XML 表,创建所有产品的列表,并显示相应的字段。问题是,我需要能够定义某些产品及其变量。

如何从 XML 产品创建类或数组,但只针对某些产品?例如,可能有 40 个产品被退回,但我可能只需要其中 3 个。数组或类必须包含产品的所有相关信息。

Here's a link to an example of the raw XML returned by the API

XML 的一个例子是

<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<NewDataSet xmlns="">
    <Table diffgr:id="Table1" msdata:rowOrder="0">
        <ChargeDescID>14249</ChargeDescID>
        <sChgDesc>Cleaning Deposit</sChgDesc>
        <dcPrice>0.0000</dcPrice>
        <dcTax1Rate>0</dcTax1Rate>
        <dcTax2Rate>0</dcTax2Rate>
        <dcInStock>0.0000</dcInStock>
    </Table>
    <Table diffgr:id="Table2" msdata:rowOrder="1">
        <ChargeDescID>14251</ChargeDescID>
        <sChgDesc>Utility Knife</sChgDesc>
        <dcPrice>2.9900</dcPrice>
        <dcTax1Rate>9</dcTax1Rate>
        <dcTax2Rate>0</dcTax2Rate>
        <dcInStock>0.0000</dcInStock>
    </Table>
</NewDataSet>

所以以上面代码中的第二个产品为例,我想要一个 PHP 函数,它可以创建一个类或这样的数组:

$utility_knife (
  "ChargeDescID" => "14251",
  "dcPrice" => "2.99",
  "dcTax1Rate" => "9",
  "dcTax2Rate" => "0",
  "dcInStock" => "0",
 )

如何提取特定的 diffgr 表并将它们格式化为命名数组或类,而忽略我不需要的表?如何在忽略其他表的情况下获取表的内容?

编辑以包含我尝试过的内容:

我已经能够使用 loadXML() 和 DOMXPath 将它们拉入一个数组,如下所示:

$dom = new DOMDocument;
$dom->loadXML($result);
$xpath = new DOMXPath($dom);
$el = $xpath->query('//Table');

#loop through results
foreach($el as $units) {

    $ChargeDescID = $xpath->query('ChargeDescID', $units)->item(0)->nodeValue;
    $sChgDesc = $xpath->query('sChgDesc', $units)->item(0)->nodeValue;
    $dcPrice = $xpath->query('dcPrice', $units)->item(0)->nodeValue;
    $dcTax1Rate = $xpath->query('dcTax1Rate', $units)->item(0)->nodeValue;
    $dcTax2Rate = $xpath->query('dcTax2Rate', $units)->item(0)->nodeValue;
    $dcInStock = $xpath->query('dcInStock', $units)->item(0)->nodeValue;

    #create oragnized array for results
    $iterate_list = array("ChargeDescID"=>$ChargeDescID,"sChgDesc"=>$sChgDesc, "dcPrice"=>$dcPrice, "dcTax1Rate"=>$dcTax1Rate, "dcTax2Rate"=>$dcTax2Rate, "dcInStock"=>$dcInStock);
    #create/append array of array
    $results_list[] = $iterate_list;

}

所以现在我有一个名为 $results_list 的数组,其中包含以 $key/$value 对组织的子数组,但数组本身没有命名。我可以像这样有条理地打印出结果:

        foreach($results_list as $key => $value) {

    echo 
        "Charge Description ID: " . $results_list[$key]["ChargeDescID"] . "<br>" .
        "Item Description: " . $results_list[$key]["sChgDesc"] . "<br>" .
        "Item Price: " . $results_list[$key]["dcPrice"] . "<br>" .
        "Tax rate 1: " . $results_list[$key]["dcTax1Rate"] . "<br>" .
        "tax rate 2: " . $results_list[$key]["dcTax2Rate"] . "<br>" .
        "In Stock: " . $results_list[$key]["dcInStock"] . "<br>"
        ;
    }

编辑:我在下面提出的这个解决方案奏效了。我目前正在使用它,但我愿意接受更优雅的解决方案。最好是直接选择节点而不需要条件逻辑的节点。 ThW 提出了一些可行的方法。我正在等待他的澄清。

问题是我不知道如何获得特定产品。我知道我可以使用数组索引,但索引可能会从一个拉到下一个。我想我需要某种类似于以下内容的函数:

foreach($results_list as $key => $value){
  if ( $results_list[$key]["sChgDesc"] == "Utility Knife" ) {
    $utility_knife = array(
      "sChgDesc" => $results_list[$key]["sChgDesc"],
      "dcPrice" => $results_list[$key]["dcPrice"],
      "dcTax1Rate" => $results_list[$key]["dcTax1Rate"],
      "dcInStock" => $results_list[$key]["dcInStock"],
  );

然后在数组中为我需要的每个产品写出 if 语句。这样对吗?

我已经尝试了很多次如何做到这一点,现在我开始感到困惑。我不确定是否可以对其中一个子数组值调用 if 语句,然后在该值存在时循环返回该特定子数组中的其余值。

需要挑选的标准是我必须能够识别我选择的产品。所以它可能依赖于 ChargeDescID 或 sChgDesc,但实际上并不依赖于其他任何东西。然后我需要确保填充了其他相关字段。

【问题讨论】:

  • 你试过什么?有很多方法可以做到这一点。您也没有给出任何标准来决定您希望如何选择 XML 中的哪个项目要转换为对象。
  • 使用simplexml函数读取你的xml dom然后创建你的数组。
  • 使用 XML 解析器。使用 XPath 获取您感兴趣的节点。就像@MikeBrant 所写,有上千种方法可以做到这一点。
  • 这些示例/概念可能对您有用:Nested XML to MySQL tables using phpSimpleXML & PHP: Extract part of XML document & convert as Array - 您可能对 XML 命名空间感兴趣。
  • 大家好,我添加了更多详细信息,解释了我已经尝试过的内容以及我认为我需要如何解决问题。您可以提供的任何反馈以及其他信息将不胜感激。谢谢。

标签: php arrays xml class loops


【解决方案1】:

您已经在使用 XPath,但它可以做的更多。 DOMXpath::query() 也是有限的。使用 DOMXpath::evaluate() - 它可以返回标量值。

例如:

$ChargeDescID = $xpath->query('ChargeDescID', $units)->item(0)->nodeValue;

可以重构为:

$ChargeDescID = $xpath->evaluate('string(ChargeDescID)', $units);

XPath 可以包含复杂的条件。假设您要获取具有diffgr:id 属性Table1Table 元素节点:

$xpath->registerNamespace('diffgr', 'urn:schemas-microsoft-com:xml-diffgram-v1');
$el = $xpath->evaluate('//Table[@diffgr:id="Table1"]');

XPath 没有默认命名空间,因此如果您想寻址不同命名空间中的节点,然后是空命名空间 (xmlns=""),您需要为其注册一个前缀。这可以是与文档中相同的前缀,也可以是不同的前缀。

另一方面,您可以按名称或更通用的方式获取节点。 * 代表任意元素节点。

$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXPath($dom);
$xpath->registerNamespace('diffgr', 'urn:schemas-microsoft-com:xml-diffgram-v1');
$el = $xpath->evaluate('//Table[@diffgr:id="Table1"]');

$results_list = [];
foreach ($el as $units) {
  $iterate_list = [];
  foreach ($xpath->evaluate('*', $units) as $valueNode) {
    $iterate_list[$valueNode->localName] = $valueNode->nodeValue;
  }
  $results_list[] = $iterate_list;
}    
var_dump($results_list);

输出:

array(1) {
  [0]=>
  array(6) {
    ["ChargeDescID"]=>
    string(5) "14249"
    ["sChgDesc"]=>
    string(16) "Cleaning Deposit"
    ["dcPrice"]=>
    string(6) "0.0000"
    ["dcTax1Rate"]=>
    string(1) "0"
    ["dcTax2Rate"]=>
    string(1) "0"
    ["dcInStock"]=>
    string(6) "0.0000"
  }
}

【讨论】:

  • 所以在你的例子中, $results_list 在这个例子中甚至不是必需的。我可以为命名不佳的 $el 变量定义多个路径吗?我的意思是我可以有 $cleaning_deposit = $xpath->evaluate('//Table[@diffgr:id="Table1"]'),然后是另一个 $utility_knife= $xpath->evaluate('//Table [@diffgr:id="Table2"]')?这样做会导致我不得不编写多个循环。我已经找到了一个适合我的解决方案,但我总是愿意重构为更好的代码。
  • 我刚刚想到了直接选择模型的另一个可能障碍。我不知道通过 API 输入新产品时 XML 文档如何更新。如果它将所有内容都下推到层次结构中,那么当添加某些内容时,它可能会导致所有产品都出错。但是,如果将新产品附加到文档末尾,它仍然可以工作。
  • XPath 可以更复杂,可以选择多个节点,是的。例如://Table[@diffgr:id="Table1" or @diffgr:id="Table2"]。如果更新更改,结果取决于更新。如果结构/格式发生变化,您将不得不调整 XPath。
【解决方案2】:

你已经尝试过这样的事情:

 $xml = simplexml_load_file('file_xml.xml');

 print '<pre>';

 print_r($xml);

print_r 仅用于调试

【讨论】:

  • 你的回答没有回答我问题的任何部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-13
  • 2011-09-06
  • 1970-01-01
  • 2019-12-22
  • 1970-01-01
相关资源
最近更新 更多