Question

i have an xml file in a special format how can i get nested tag value out of it for example if i want opacity, Thickness, repeat, rotation tags with value 1, 0, false, 90 respectively for first dict tag and same for the others. please point me in right direction

<?xml version="1.0" encoding="UTF-8"?>
<pu version="1.0">
  <dict>
    <key>ID</key>
    <string>C0AC8773-CEE6-4A12-9C69-320A1BDB7255</string>
    <key>Items</key>
    <array>
      <dict>
        <key>opacity</key>
        <real>1</real>
        <key>Thickness</key>
        <real>0</real>
        <key>repeat</key>
        <false/>
        <key>rotation</key>
        <real>90</real>
      </dict>
      <dict>
        <key>opacity</key>
        <real>1</real>
        <key>Thickness</key>
        <real>0</real>
        <key>repeat</key>
        <false/>
        <key>rotation</key>
        <real>180</real>
      </dict>
      <dict>
        <key>opacity</key>
        <real>1</real>
        <key>Thickness</key>
        <real>0</real>
        <key>repeat</key>
        <false/>
        <key>rotation</key>
        <real>270</real>
      </dict>
    </array>
  </dict>
</pu>

this is what i want

$dom = new DOMDocument;
$dom->load($path);
$array = $dom->getElementsByTagName('array');
foreach($array as $dict)
{
   foreach($dict as $key->$value)
    {
      print_r("key = ".$key." value = ".$value);//i.e. key = opacity value = 1
    }
}

what am i doing wrong and thanks in advance

Was it helpful?

Solution

$dom = new DOMDocument;
$dom->loadXML($xml);
$xpath = new DOMXpath($dom);
$dicts = $xpath->query("/pu/dict/array/dict");

foreach($dicts as $dict) { 
    $childNodes = $dict->childNodes;
    foreach ($childNodes as $child) {
        if ($child->nodeName !== 'key') {
            continue;
        }
        echo $child->nodeValue . " " . getNextSibling($child) . "<br>";
    }
}

function getNextSibling($node) {
    return ($node->nodeType === 3) ? $node->nextSibling->nodeValue : $node->nextSibling->nextSibling->nodeValue;
}

See it in action

OTHER TIPS

Here are two jobs. One you have to iterate the dict elements and get all key elements. Second get the value node for each key element and parse it depending on its name.

DOMXpath::evaluate() or query() can take a second argument with a context node. The Xpath expression will be relative to it.

On the root level /pu/dict fetches all dict children of the document element.

$result = [];
$dicts = $xpath->evaluate('/pu/dict');
foreach ($dicts as $dict) {
  $result[] = getList($xpath, $dict);
}

Inside the getList() function key is used to get the key child elements of a dict node.

function getList($xpath, $node) {
  $items = $xpath->evaluate('key', $node);
  $result = [];
  foreach ($items as $item) {
    $result[$item->nodeValue] = getValue($xpath, $item);
  }
  return $result;
}

The value node is the element following each key element. In Xpath following-sibling::*[1]. The value type depends on the node name. Most are just scalar values. If it is array, fetch the dict element children and call getList() for each of them.

function getValue($xpath, $node) {
  $valueNode = $xpath->evaluate('following-sibling::*[1]', $node)->item(0);
  switch ($valueNode->nodeName) {
  case 'array' : 
    $dicts = $xpath->evaluate('dict', $valueNode);
    foreach ($dicts as $dict) {
      $result[] = getList($xpath, $dict);
    }
    return $result;
  case 'true' :
    return TRUE;
  case 'false' :
    return FALSE;
  case 'real' :
    return (float)$valueNode->nodeValue;
  case 'string' :
    return $valueNode->nodeValue;
  }
}

Put together: https://eval.in/private/b88a430a237494

$xml = <<<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<pu version="1.0">
  <dict>
    <key>ID</key>
    <string>C0AC8773-CEE6-4A12-9C69-320A1BDB7255</string>
    <key>Items</key>
    <array>
      <dict>
        <key>opacity</key>
        <real>1</real>
        <key>Thickness</key>
        <real>0</real>
        <key>repeat</key>
        <false/>
        <key>rotation</key>
        <real>90</real>
      </dict>
      <dict>
        <key>opacity</key>
        <real>1</real>
        <key>Thickness</key>
        <real>0</real>
        <key>repeat</key>
        <false/>
        <key>rotation</key>
        <real>180</real>
      </dict>
      <dict>
        <key>opacity</key>
        <real>1</real>
        <key>Thickness</key>
        <real>0</real>
        <key>repeat</key>
        <false/>
        <key>rotation</key>
        <real>270</real>
      </dict>
    </array>
  </dict>
</pu>
XML;

$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);

function getValue($xpath, $node) {
  $valueNode = $xpath->evaluate('following-sibling::*', $node)->item(0);
  switch ($valueNode->nodeName) {
  case 'array' : 
    $dicts = $xpath->evaluate('dict', $valueNode);
    foreach ($dicts as $dict) {
      $result[] = getList($xpath, $dict);
    }
    return $result;
  case 'true' :
    return TRUE;
  case 'false' :
    return FALSE;
  case 'real' :
    return (float)$valueNode->nodeValue;
  case 'string' :
    return $valueNode->nodeValue;
  }
}

function getList($xpath, $node) {
  $items = $xpath->evaluate('key', $node);
  $result = [];
  foreach ($items as $item) {
    $result[$item->nodeValue] = getValue($xpath, $item);
  }
  return $result;
}

$result = [];
$dicts = $xpath->evaluate('/pu/dict');
foreach ($dicts as $dict) {
  $result[] = getList($xpath, $dict);
}

var_dump($result);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top