Php 使用xpath删除CDATA的SimpleXMLElement
我需要将XML的一个节点递归地转换为json字符串。我大部分时间都有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) { $
$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
然后由于对它提供的魔法属性的公共遍历,负责所有子级
另见:
@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]]);
}