0

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

我现在的问题是,如何处理不同的子节点。正如您在提供的 XML 片段中看到的那样,可能只有一个“名称”节点,但有时会有两个甚至更多。当有多个这样的节点时,我必须根据“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似乎很相似,但它并没有真正解决子节点数量不同的部分以及选择特殊子节点的任务。

4

2 回答 2

1

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

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

为了进行比较,这里是您的代码的现场演示,其中 XML 片段合并到一个 XML 文档中。

使用 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给出了这个版本(现场演示):

$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;内部循环中的 ,以阻止echo一个产品的多个名称(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";
}
于 2013-09-12T22:15:34.737 回答
0

我找不到合适的方法使用SimpleXML. 我比较熟悉DomDocument和它的loadXML()方法load

而不是将其更改为数组,只需使用getElementsByTagName().

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

于 2013-09-12T09:34:30.033 回答