使用xpath删除CDATA的SimpleXMLElement



我需要递归地将XML的一个节点转换为json字符串。我有大部分

$sku = "AC2061414";
$dom = new SimpleXMLElement(file_get_contents( "/usr/share//all_products.xml" )); 
$query = '//sku[text() = "'.$sku.'"]';
$entries = $dom->xpath($query);
foreach ($entries as $entry) {
    $parent_div = $entry->xpath( 'parent::*' );
    $nodearray=array();
    foreach($parent_div as $node) {
        if ($node->nodeType == XML_CDATA_SECTION_NODE) {
            $nodearray[$node->getName()]=$node->textContent;
        }else{
            $nodearray[$node->getName()]=$node;
        }
    }
    $ajax = json_encode( $nodearray );
    print($ajax);
}

在上运行

<?xml version="1.0" encoding="UTF-8"?>
<products>
   <product active="1" on_sale="0" discountable="1">
    <sku>AC2061414</sku>
    <name><![CDATA[ALOE CADABRA ORGANIC LUBE PINA COLADA 2.5OZ]]></name>
    <description><![CDATA[ text text ]]></description>
    <keywords/>
    <price>7.45</price>
    <stock_quantity>30</stock_quantity>
    <reorder_quantity>0</reorder_quantity>
    <height>5.25</height>
    <length>2.25</length>
    <diameter>0</diameter>
    <weight>0.27</weight>
    <color></color>
    <material>aloe vera, vitamin E</material>
    <barcode>826804006358</barcode>
    <release_date>2012-07-26</release_date>
    <images>
      <image>/AC2061414/AC2061414A.jpg</image>
    </images>
    <categories>
      <category code="528" video="0" parent="0">Lubricants</category>
      <category code="531" video="0" parent="528">Flavored</category>
      <category code="28" video="0" parent="25">Oral Products</category>
      <category code="532" video="0" parent="528">Natural</category>
    </categories>
    <manufacturer code="AC" video="0">Aloe Cadabra Lubes</manufacturer>
    <type code="LU" video="0">Lubes</type>
  </product>
</products>

以结束

{"product":{"@attributes":{"active":"1","on_sale":"0","discountable":"1"},"sku":"AC2061414","name":{},"description":{},"keywords":{},"price":"7.45","stock_quantity":"30","reorder_quantity":"0","height":"5.25","length":"2.25","diameter":"0","weight":"0.27","color":{},"material":"aloe vera, vitamin E","barcode":"826804006358","release_date":"2012-07-26","images":{"image":"/AC2061414/AC2061414A.jpg"},"categories":{"category":["Lubricants","Flavored","Oral Products","Natural"]},"manufacturer":"Aloe Cadabra Lubes","type":"Lubes"}}

除了缺少CDATA的节点值之外,这似乎还可以。我确实试着解释了一下,但没有用。这里有什么诀窍?

您可以尝试将LIBXML_NOCDATA选项添加到构造函数中。

$dom = new SimpleXMLElement(file_get_contents( "/usr/share//all_products.xml" ), LIBXML_NOCDATA);
...

更多详细信息请点击此处。

这里的问题是因为json_encode,它根据它们神奇的接口来处理您所拥有的simplexml元素。例如,请参阅序列化@attributes。并且还跳过所有子cdata节点,因为在魔术模式下读取元素值时(比较simplexmlelements的print_rvar_dump输出),这些节点会被丢弃。

由于CDATA节点可以标准化为周围的文本,也可以仅标准化为常见的文本节点,SimpleXML提供了LIBXML_NOCDATA选项(在使用newsimplexml_load_*函数实例化时)来做到这一点:将这些CDATA节点转换为文本节点,并将这些文本节点合并为周围的文字节点(如果有的话)("merge CDATA as text nodes")。

这将使print_rjson_encode以字符串@属性的形式返回节点值,因为现在它是节点值。"PHP,SimpleXML,解码CDATA中的实体"中对此进行了详细解释。

除此之外,还有另一个误解,您可以从中受益匪浅。即使您的代码已经包含通过属性值选择元素的xpath,您也更感兴趣的是它的父级。然后,SimpleXML将为所有子级提供迭代。SimpleXML对于json_encode的神奇特性也是如此。比较一下这是如何让你减少代码的:

$xml = simplexml_load_file("/usr/share/all_products.xml", NULL, LIBXML_NOCDATA); 
// NOTE: Prevent XPath Injection by not allowing " (or ') for 
//       SKU value (validate it against a whitelist of allowed
//       characters for example)
$sku   = "AC2061414";
$query = sprintf('(//sku[text() = "%s"])[1]/..', $sku); 
$products = $xml->xpath($query);
if ($products) {
    echo json_encode(["product" => $products[0]]);
}

请参阅演示。

这应该可以在不编写那么多代码的情况下为您提供相等的输出。在创建SimpleXMLElement时,请参阅LIBXML_NOCDATA选项以及修改后的xpath查询,该查询将直接查询有问题的(第一个)sku元素的父节点(<product>)。json_encode然后照顾所有的孩子,因为它提供了魔法属性的公共遍历。

另请参阅:

  • Bug#41976:json_encode()忽略SimpleXML数据中的CDATA

相关内容

  • 没有找到相关文章

最新更新