我正在读取一个xml文件,该文件返回xml的SimpleXMLElement对象表示。我将采用一个数组并为该对象提供新值。我不知道我将在那个数组中做什么。
如果我蛮力,我会做这样的事情。
//Solution 1: Brute Force
//Just creating an array and value for purposes of demonstration.
$arOfData = array( [0]=>"theFirstNode", [1]=>"theSecondNode",[2]=>"theThirdNode" );
$value = "The XML Node Value";
$simpleXml->$arOfData[0]->$arOfData[1]->$arOfData[2] = $value;
//The next best thing I can think of doing is something like this.
//Solution 2: Semi-brute force
//
foreach($this->arrayData as $key => $value) {
$xmlNodes = explode( '-', $key);
$numNodes = count($xmlNodes);
switch($numNodes) {
case 1:
$simpleXml->$xmlNodes[0] = $value;
break;
case 2:
$simpleXml->$xmlNodes[0]->$xmlNodes[1] = $value;
break;
case 3:
$simpleXml->$xmlNodes[0]->$xmlNodes[1]->$xmlNodes[2] = $value;
break;
case 4:
$simpleXml->$xmlNodes[0]->$xmlNodes[1]->$xmlNodes[2]->$xmlNodes[3] = $value;
break;
case 5:
$simpleXml->$xmlNodes[0]->$xmlNodes[1]->$xmlNodes[2]->$xmlNodes[3]->$xmlNodes[4] = $value;
break;
}
}
* note此解决方案使用数组键并将其展开为由短划线分隔的数组,然后将该数组值用作新的xml值。所以不要让它分散你的注意力。
解决方案#2的问题是:当我们得到一个深度超过5的xml节点时会发生什么?它不会被塞进我们正在创建的新对象中。哦,哦。它也不是很优雅;)。我不知道如何以更加递归的方式做到这一点。
答案 0 :(得分:1)
就像你在问题中写的一样,你需要动态地拥有它,因为你不知道父元素的数量。
您需要深入了解simpexml如何完成这项工作。
但首先让我建议你有一个不同的符号,不是你的减号,而是路径中的斜线。
first/second/third
这在Xpath中也很常见,我认为它很适合自己。减号也可以是元素名称的一部分,但斜杠不能。所以这只是更好一点。
在我向您展示如何轻松访问 <third>
元素节点以设置其值之前,首先让我们看一下simplexml中的一些分配基础。
要在SimpleXMLElement中访问和设置此元素节点,请参阅以下示例:
$xml = new SimpleXMLElement('<root><first><second><third/></second></first></root>');
$element = $xml->first->second->third;
$element[0] = "value";
这很简单,但你可以在这里看到两件事:
<third>
元素已存在于文档中。[0]
),它允许设置元素变量的XML值(而不是变量)。这特定于 SimpleXMLElement 的工作方式。第二点还包含如何处理不存在的元素的问题的解决方案。如果元素不存在,$element[0]
为NULL
:
$xml = new SimpleXMLElement('<root><first><second/></first></root>');
$element = $xml->first->second->third;
var_dump($element[0]); # NULL
因此,让我们尝试有条件地添加第三个元素,以防它不存在:
if ($xml->first->second->third[0] === NULL) {
$xml->first->second->third = "";
}
这确实解决了这个问题。因此,唯一要做的就是以迭代的方式为路径的所有部分做到这一点:
first/second/third
为了保持这一点,请为此创建一个函数:
/**
* Modify an elements value specified by a string-path.
*
* @param SimpleXMLElement $parent
* @param string $path
* @param string $value (optional)
*
* @return SimpleXMLElement the modified element-node
*/
function simplexml_deep_set(SimpleXMLElement $parent, $path, $value = '')
{
### <mocked> to be removed later: ###
if ($parent->first->second->third[0] === NULL) {
$parent->first->second->third = "";
}
$element = $parent->first->second->third;
### </mocked> ###
$element[0] = $value;
return $element;
}
因为该函数是模拟的,所以可以直接使用:
$xml = new SimpleXMLElement('<root><first><second/></first></root>');
simplexml_deep_set($xml, "first/second/third", "The XML Node Value");
$xml->asXML('php://output');
这有效:
<?xml version="1.0"?>
<root><first><second><third>The XML Node Value</third></second></first></root>
所以现在删除模拟。首先插入 explode ,就像你拥有它一样。然后,所有需要做的就是沿着路径的每一步进行,并且如果它不存在则有条件地创建元素。最后 $element
将是要修改的元素:
$steps = explode('/', $path);
$element = $parent;
foreach ($steps as $step)
{
if ($element->{$step}[0] === NULL) {
$element->$step = '';
}
$element = $element->$step;
}
需要使用 foreach 将mock替换为工作版本。与全功能定义一目了然:
function simplexml_deep_set(SimpleXMLElement $parent, $path, $value = '')
{
$steps = explode('/', $path);
$element = $parent;
foreach ($steps as $step)
{
if ($element->{$step}[0] === NULL) {
$element->$step = "";
}
$element = $element->$step;
}
$element[0] = $value;
return $element;
}
让我们修改更多疯狂的东西来测试它:
$xml = new SimpleXMLElement('<root><first><second/></first></root>');
simplexml_deep_set($xml, "first/second/third", "The XML Node Value");
simplexml_deep_set(
$xml, "How/do/I/dynamically/create/a/php/simplexml/object/while/keeping/current/properties"
, "The other XML Node Value"
);
$xml->asXML('php://output');
示例 - 输出(美化):
<?xml version="1.0"?>
<root>
<first>
<second>
<third>The XML Node Value</third>
</second>
</first>
<How>
<do>
<I>
<dynamically>
<create>
<a>
<php>
<simplexml>
<object>
<while>
<keeping>
<current>
<properties>The other XML Node Value</properties>
</current>
</keeping>
</while>
</object>
</simplexml>
</php>
</a>
</create>
</dynamically>
</I>
</do>
</How>
</root>