Php 使用xpath删除CDATA的SimpleXMLElement

Php 使用xpath删除CDATA的SimpleXMLElement,php,xml,json,xpath,simplexml,Php,Xml,Json,Xpath,Simplexml,我需要将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) { $

我需要将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>

除了CDATA中缺少的节点值之外,其他节点值似乎都正常。我确实试着解释一下,但它不起作用。这里的诀窍是什么?

您可以尝试向构造函数添加
LIBXML\u NOCDATA
选项

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

更多详细信息。

这里的问题是因为
json\u encode
,它根据SimpleXML元素的神奇接口处理它们。例如,请参见序列化
@attributes
。并且还跳过所有子cdata节点,因为在magic模式下读取元素值时(比较simplexmlelements的
print\r
var\u dump
输出),这些节点会被删除

因为SimpleXML提供了(在实例化时使用
new
SimpleXML\u load.*
函数)来实现这一点:将这些CDATA节点转换为文本节点,并将这些文本节点合并为周围的文本节点(如果有)(“将CDATA合并为文本节点”)

这将使
print\u r
json\u encode
然后将节点值作为string@attributes返回,因为现在它是节点值。这一点已在中详细解释(很好)

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

$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\u NOCDATA
选项以及修改后的xpath查询,该查询将直接查询(第一个)sku元素的父节点(
json_encode
然后由于对它提供的魔法属性的公共遍历,负责所有子级

另见:


这里的扩展解释非常好,是的,我确实知道我已经扩展了节点的横向,但是我想不出还有什么其他方法可以基于子节点添加一个条件来隔离问题的根源。我离你在那里的位置不远,但可以肯定的是,这是一条更好的道路。我会留下第一个答案作为被接受的答案,因为它是在这个哈特,但到目前为止,这是更多的信息给读者,谢谢你。谢谢你的评论,我有点这样认为。对于XML,我们需要围绕层次结构展开讨论,而对于JSON,这是类似的。我很有兴趣在这里摆弄你的,我也能够挖掘出一些现有的信息,希望是有用的。有一件事我有点好奇:对于进入json_encode的元素,@attributes属性被创建,但只为这个元素创建,而不是为子元素创建。我不确定这是否也是你的问题的一部分。老实说,我不介意让
@attributes
保持不变,但在这种情况下,这不是生死攸关,而是从未来的角度来看,奇怪的是它会放弃它们。我相信那里有一辆小车,因为它应该递归地处理这个问题。嗯,至少是这样。如果你有想法,我肯定会欢迎你的。谢谢是的,到目前为止我还没有找到一个解释来解释为什么会发生这种情况。虽然这看起来并不明显,但可能有一个很好的理由。但到目前为止,我还没有找到好的线索,但我可能会在以后把这些零碎的东西捡起来。如果你想知道怎么做。如果您知道使用PHP可以实现这一点,这可能是件好事。关于@attributes以及对第一个元素和遍历元素的不同处理,我可以对其进行一番总结,我想我可以对其进行一些解释:。看来这是一种折衷/妥协。我计划写第二部分,展示我在另一个答案中链接的内容。我知道这不是你真正想问的,但你为什么要将XML翻译成JSON呢?为什么不将节点序列化为XML并在下一个处理阶段进行解析?@IMSoP它的不足之处在于它是用于一个临时视图系统的,并且由于XML是40mb,因此创建小型json文件以供以后使用会更快。。。基本上,尽管看起来很奇怪,它简化了所有范围内的事情。这里更具体的项目,所以我没有详细说明整个过程,只是我需要的部分。@jeremyBass\u DC足够公平。您仍然可以创建min-XML文件而不是mini-JSON,不过-只要想想
“@attributes”
键就可以有效地将您与SimpleXML绑定。@IMSoP是的,您是对的,我可以将它们转换为mini-XML包,但是javascript让我发送json,那么为什么不在xml已经在内存中的时候执行这一步骤呢?还有什么不可以把它推下去呢?因为现在我可以只处理字符串了。这是一种工作的扩散,实际上,这是一种偏好,但对于解决这个问题的普通人来说,重要的是LIBXML_NOCDATA标志很重要。谢谢你的邀请
$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]]);
}