1

我正在读取一个 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 节点时会发生什么?它不会被塞进我们正在创建的新对象中。哦哦。它也不是很优雅;)。我不确定如何以更递归的方式执行此操作。

4

1 回答 1

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";

这很简单,但您可以在这里看到两件事:

  1. <third>元素已存在于文档中。
  2. 该代码用作 simplexml-self-reference ( [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;
}

因为函数是mock的,所以可以直接使用:

$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>

所以现在删除模拟。首先插入爆炸,就像你有它一样。然后需要做的就是沿着路径的每一步走,如果元素还不存在,有条件地创建它。最后$element将是要修改的元素:

$steps   = explode('/', $path);

$element = $parent;
foreach ($steps as $step)
{
    if ($element->{$step}[0] === NULL) {
        $element->$step = '';
    }

    $element = $element->$step;
}

这个foreach需要用工作版本替换模拟。与完整的函数定义一目了然:

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>

在行动中看到它。

于 2013-04-12T21:21:00.187 回答