php - Get all leaf from complex XML with attributes -
i have kind of xml file
myxml.xml
<?xml version="1.0" encoding="utf-8"?> <products nb="2" type="new"> <product ean="12345677654321"> <sku>product1</sku> <parameters> <short_desc> short description of product1 </short_desc> <price currency="usd">19.65</price> </parameters> </product> <product ean="12345644654321"> <sku>product2</sku> <parameters> <long_desc> long description of product2 </long_desc> <price currency="usd">19.65</price> <vat>20</vat> </parameters> </product> </products> i array this
/products/@nb /products/@type /products/product/@ean /products/product/sku /products/product/parameters/short_desc /products/product/parameters/long_desc /products/product/parameters/price /products/product/parameters/price/@currency /products/product/parameters/vat i result code
getpath.xsl
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/xsl/transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:strip-space elements="*"/> <xsl:variable name="vapos">'</xsl:variable> <xsl:template match="*[@* or not(*)] "> <xsl:if test="not(*)"> <xsl:apply-templates select="ancestor-or-self::*" mode="path"/> <xsl:text>
</xsl:text> </xsl:if> <xsl:apply-templates select="@*|*"/> </xsl:template> <xsl:template match="*" mode="path"> <xsl:value-of select="concat('/',name())"/> <xsl:variable name="vnumprecsiblings" select= "count(preceding-sibling::*[name()=name(current())])"/> <xsl:if test="$vnumprecsiblings"> <xsl:value-of select="concat('[', $vnumprecsiblings +1, ']')"/> </xsl:if> </xsl:template> <xsl:template match="@*"> <xsl:apply-templates select="../ancestor-or-self::*" mode="path"/> <xsl:value-of select="concat('/@',name())"/> <xsl:text>
</xsl:text> </xsl:template> </xsl:stylesheet> $xsldoc = new \domdocument(); $xsldoc->substituteentities = true; $xsldoc->load('getpath.xsl'); $xmldoc = new \domdocument(); $xmldoc->load('myxml.xml'); $proc = new \xsltprocessor(); $proc->importstylesheet($xsldoc); $rest = $proc->transformtoxml($xmldoc); $res = preg_replace("/\\s/"," ", $rest); $path = explode(" ", $res); foreach ($path $key => $value) { if(!empty($value) && !preg_match("/\[.*\]/", $value)) $fields[] = $value; } return $fields; this code give me
/products/@nb /products/@type /products/product/@ean /products/product/sku /products/product/parameters/short_desc /products/product/parameters/price /products/product/parameters/price/@currency /products/product/parameters/long_desc , /products/product/parameters/price/vat missing :(
how can parse full xml xslt ? or have solution without xslt ???
yeah can xpath in php.
$dom = new domdocument(); $dom->loadxml($xml); $xpath = new domxpath($dom); function getnodeexpression(domnode $node, array &$namespaces) { $name = $node->localname; $namespace = $node->namespaceuri; if ($namespace == '') { return ($node instanceof domattr ? '@' : '').$name; } elseif (isset($namespaces[$namespace])) { $prefix = $namespaces[$namespace]; } else { $xmlns = $prefix = ($node->prefix == '') ? 'ns' : $node->prefix; $i = 1; while (in_array($xmlns, $namespaces)) { $xmlns = $prefix.'-'.$i; $i++; } $namespaces[$namespace] = $prefix; } return ($node instanceof domattr ? '@' : '').$prefix.':'.$name; } $result = []; $namespaces= []; foreach ($xpath->evaluate('//*[count(*) = 0]|//@*') $node) { $path = ''; foreach ($xpath->evaluate('ancestor::*', $node) $parent) { $path = '/'.getnodeexpression($parent, $namespaces); } $path .= '/'.getnodeexpression($node, $namespaces); $result[$path] = true; } output: https://eval.in/118054
array(10) { [0]=> string(13) "/products/@nb" [1]=> string(15) "/products/@type" [2]=> string(13) "/product/@ean" [3]=> string(12) "/product/sku" [4]=> string(22) "/parameters/short_desc" [5]=> string(17) "/parameters/price" [6]=> string(16) "/price/@currency" [7]=> string(21) "/parameters/long_desc" [8]=> string(20) "/long_desc/@xml:lang" [9]=> string(15) "/parameters/vat" } array(1) { ["http://www.w3.org/xml/1998/namespace"]=> string(3) "xml" } the complex part @ resolve namespaces , generate prefixes them. let's take detailed look:
get local name (tag name without namespace prefix) , namespace.
$name = $node->localname; $namespace = $node->namespaceuri; if namespace empty not need prefix return expression node name.
if ($namespace == '') { return ($node instanceof domattr ? '@' : '').$name; otherwise check if namespace used on node , reuse prefix.
} elseif (isset($namespaces[$namespace])) { $prefix = $namespaces[$namespace]; if here unknown namespace, read prefix used on node. if node didn't use prefix use string "ns".
} else { $xmlns = $prefix = ($node->prefix == '') ? 'ns' : $node->prefix; validate prefix not used namespace add number , increase until have unique prefix.
$i = 1; while (in_array($xmlns, $namespaces)) { $xmlns = $prefix.'-'.$i; $i++; } store namespace => prefix definition next call.
$namespaces[$namespace] = $prefix; return expression including prefix.
return ($node instanceof domattr ? '@' : '').$prefix.':'.$name; the namespace array can used register needed namespace prefix on xpath object.
Comments
Post a Comment