【问题标题】:Flatten XML with varying child node structure using PHP使用 PHP 用不同的子节点结构展平 XML
【发布时间】:2013-09-16 14:35:37
【问题描述】:

我必须解析和展平一个由许多单一产品组成的 XML 文件。 XML 有完整的文档,使用 SimpleXML 可以很容易地在 PHP 中解析 XML。请参阅下面的代码,我是如何从单个产品创建数组的。然后我访问所有必需的密钥并将数据存储在 SQL 数据库中。

我现在的问题是,如何处理不同的子节点。正如您在提供的 XML sn-ps 中看到的,可能有一个“名称”节点,但有时会有两个甚至更多。当有多个这样的节点时,我必须根据“NameType”来决定使用哪个“NameText”。 “价格”节点也是如此。

<Product>
  <Id>123</Id>
  <Name>
    <NameType>3</NameType>
    <NameText>Hello World</NameText>
  </Name>
  <Price>
    <Country>US</Country>
    <Amount>9.90</Amount>
  </Price>
</Product>

<Product>
  <Id>124</Id>
  <Name>
    <NameType>1</NameType>
    <NameText>Goodbye Cruel World</NameText>
  </Name>
  <Name>
    <NameType>3</NameType>
    <NameText>Goodbye Cruel World, I'm Leaving You Today</NameText>
  </Name>
  <Name>
    <NameType>9</NameType>
    <NameText>Goodbye</NameText>
  </Name>
  <Price>
    <Country>CAN</Country>
    <Amount>27.90</Amount>
  </Price>
  <Price>
    <Country>US</Country>
    <Amount>19.90</Amount>
  </Price>
</Product>

这是我处理此问题的代码:我将 XML 转换为关联数组,然后使用大量 if-magic 来获取我需要的数据。提供的代码打印出第一个产品示例的“Hello World”和第二个产品示例的“Goodbye Cruel World”。

$xml = simplexml_load_string($product);
$json = json_encode($xml);
$arr = json_decode($json, True);
// $arr['Name']['NameText'] contains the single NameText for this product in example one
// $arr['Name'][0]['NameText'] contains the first of three NameTexts in example two

if( array_key_exists(0, $arr['Name']) ) {
  foreach( $arr['Name'] as $n) {
    if( $n['NameType'] == 1 ) {
      echo $n['NameText']."\n";
      break;
    } elseif ( $n['NameType'] == 3 ) {
      echo $n['NameText']."\n";
      break;
    }
  }
} else {
  echo $arr['Name']['NameText']."\n";
}

虽然此代码有效,但我对可能多次出现的所有节点的逐个案例分析并不十分满意。而且我什至必须依赖子节点的“正确”顺序,假设 NameType“1”总是发生在 NameType“3”之前。所以我倾向于希望有一个更聪明的解决方案。

XML with varying amount of child nodes for each parent node 的问题似乎很相似,但它并没有真正解决子节点数量不同的部分以及选择特殊子节点的任务。

【问题讨论】:

    标签: php xml simplexml flatten


    【解决方案1】:

    我找不到使用SimpleXML 的合适方法。我更熟悉DomDocument 及其loadXML()load 方法。

    不要将其更改为数组,只需使用getElementsByTagName() 获取您想要的孩子。

    Nest foreach 在需要的地方循环,它应该根据需要迭代多次。 所以这解决了逐案分析,依靠文档按特定顺序提供元素。

    【讨论】:

    • 好主意,使用getElementsByTagName() 确实可以简化我的代码。但是有一个问题:我不能确定“名称”不会在“产品”下面的某个地方使用,也许在另一个子节点中。 getElementsByTagName() 也会匹配这些事件。
    【解决方案2】:

    我并不完全清楚你想要做什么(你没有对所需的输出给出明确的解释),但我会给你一些指示:

    • 放弃转换为数组(json_decode(json_encode()) hack)。您所做的只是丢弃 SimpleXML 提供的额外功能,并可能丢弃部分 XML 数据。
    • SimpleXML 的一个很好的功能是你可以写$xml-&gt;Product-&gt;Name,这意味着第一个(0th,如果你喜欢)Name 在第一个Product 上,$xml-&gt;Product[0]-&gt;Name[0] 也是如此- 不管实际上是否有多个Products 和Names。
    • 您还可以按照您所期望的方式使用 foreach ( $xml-&gt;Product as $product ) - 同样,无论该特定文档中是否有多个 Product 节点,它都有效。
    • 如果您不介意学习新语法,XPath 可用于根据节点的值查找节点。在 SimpleXML 中,您可以从任何节点(例如,特定的 Product)开始,并使用 the -&gt;xpath() method 来获取从该节点开始的“搜索结果”的普通数组。
    • 您的代码也有一些不必要的重复,因为elseifif 执行相同的代码,因此您可以只使用或(||)。 (我不确定这是否只是匿名化的结果。)

    为了比较,here's a live demo of your code, with the XML snippets combined into one XML document.

    使用 SimpleXML 本身,而不仅仅是解析为数组,您可以将其简化为这样 (Live Demo):

    $xml = simplexml_load_string($xml_data);
    
    foreach ( $xml->Product as $product )
    {
        foreach ( $product->Name as $name )
        {
            if ( $name->NameType == 1 || $name->NameType == 3 )
            {
                echo $name->NameText."\n";
                break;
            }
        }
    }
    

    使用简单的 XPath 表达式代替内部的 if 给出了这个版本 (Live Demo):

    $xml = simplexml_load_string($xml_data);
    
    foreach ( $xml->Product as $product )
    {
        foreach ( $product->xpath('Name[NameType=1 or NameType=3]') as $name )
        {
            echo $name->NameText."\n";
            break;
        }
    }
    

    或者你可以一直把所有的逻辑放到一个 XPath 表达式中——注意最后的[1],它相当于内部循环中的break;,以阻止多个名称成为@987654346 @d 代表一种产品 (Live Demo):

    $xml = simplexml_load_string($xml_data);
    
    foreach ( $xml->xpath('Product/Name[NameType=1 or NameType=3][1]') as $name )
    {
        echo $name->NameText."\n";
    }
    

    【讨论】:

    • 谢谢,我不知道可以迭代 SimpleXML 对象,这将真正简化事情。我的目标是扁平化 XML 并将一些数据存储在 SQL 数据库中,有时我必须根据 XML 中的数据做出决策。我试图以一种简单的方式复制我更复杂的现实世界任务,这导致了古怪的 if-elseif 子句。
    猜你喜欢
    • 1970-01-01
    • 2018-08-25
    • 1970-01-01
    • 2021-06-23
    • 2012-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多