使用php中的simplexml从plist-style xml获取键/值对

时间:2010-05-19 08:18:25

标签: php xpath simplexml plist

以下是xml文件中的示例位:

<array>
    <dict>
        <key>Name</key>
        <string>Joe Smith</string>
        <key>Type</key>
        <string>Profile</string>
        <key>Role</key>
        <string>User</string>
        <key>Some Number</key>
        <integer>1</integer>
        <key>Some Boolean</key>
        <true/>
    </dict>
</array>

我有两个不同的目标。第一种是从dict节点中提取一个看起来像的数组:

[Name] => Joe Smith
[Type] => Profile
[Role] => User
[Some Number] => 1
[Some Boolean] => true

包含布尔值并不重要,所以如果这增加了太多的复杂性,我宁愿知道如何处理其他人。

第二个目标是能够选择值节点(<string><integer>等),以便我可以更改值。我知道我需要根据前面关键元素的文本值选择它。

我认为以下XPath应该有效:

//key[.=$keyname]/following-sibling[1]

但我不确定。

基本上,Apple使用的整个系统看似合乎逻辑,但完全违背了我对XML应该如何工作的理解。如果我运行世界,原始XML看起来更像:

<dict type="array">
    <value key="Name" type="string">Joe Smith</value>
    <value key="Type" type="string">Profile</value>
    <value key="Role type="string">User</value>
    <value key="Some Number" type="integer">1</value>
    <value key="Some Boolean" type="boolean">true</value>
</dict>

但由于这是合乎逻辑的,我想知道我是否遗漏了一些明显的处理方式。

2 个答案:

答案 0 :(得分:3)

那个xpath选择器看起来对我来说没问题。你可能想要检查你检索的兄弟的值,看看它是你想要改变的标签(在布尔情况下)还是元素的值。

在将数据提取到数组方面,类似于...

$xml = new SimpleXMLElement($data);

$output = array();
$key = null;
$value = null;
foreach ($xml->dict->children() as $child) {
  if ($child->getName() == 'key') {
    $key = (string)$child;
  } elseif (!is_null($key)) {
    $childStr = (string)$child;
    $value = (empty($childStr) ? $child->getName() : $childStr);
  }

  if (!is_null($key) && !is_null($value)) {
    $output[$key] = $value;

    $key = null;
    $value = null;
  }
}

var_dump($output);

......应该根据我的理解做你正在寻找的事情。

詹姆斯

答案 1 :(得分:1)

您可以使用

/plist/dict/key[.='Name']/following-sibling::*[1]

选择字符串节点“Joe Smith”并相应地更新它。

我使用XPath更新XML文件的PHP代码如下所示:

function updateNodesByXPath($xmlin, $expression, $value) {
    $dom = new DOMDocument();
    $dom->preserveWhiteSpace = true;
    $dom->formatOutput = false;
    $dom->loadXML($xmlin);
    $xpath = new DOMXPath($dom);
    $elements = $xpath->query($expression);
    foreach ($elements as $element) {
        $element->nodeValue = $value;
    }
    $xmlout = $dom->saveXML($dom, LIBXML_NOEMPTYTAG);

    // Add XML header when present before (DOMDocument::saveXML will remove it)
    if (strncasecmp($xmlin, '<?xml', 5)) {
        $xmlout = preg_replace('~<\?xml[^>]*>\s*~sm', '', $xmlout);
    }

    return $xmlout;
}