获取 xml 中的特定节点?

Get specific node in xml?

我的xml结构如下:

<?xml version="1.0" ?>
<users>
    <user>
        <name>
            foo
        </name>
        <token>
            jfhsjfhksdjfhsjkfhksjfsdk
        </token>
        <connection>
            <host>
                localhost
            </host>
            <username>
                root
            </username>
            <dbName>
                Test
            </dbName>
            <dbPass>
                123456789
            </dbPass>
        </connection>
    </user>
    <user>
        ... same structure...
    </user>
</users>

我编写了遍历所有 xml 节点的代码:

function getConString($node)
{
   $item = file_get_contents($_SERVER['DOCUMENT_ROOT'] . "con");
   $nodes = new SimpleXMLElement($item);
   $result = $nodes[0];

   foreach($result as $item => $value)
   {
      if($item == "token")
      {
         return $value->__toString();
     }
   }
}

我想要实现的是当 $node 等于:

jfhsjfhksdjfhsjkfhksjfsdk

连接节点以数组形式返回,我该如何实现?

更新:

对于不理解的人,我只是希望如果我插入令牌:jfhsjfhksdjfhsjkfhksjfsdk 将返回用户与令牌 jfhsjfhksdjfhsjkfhksjfsdk 的连接。所以在这种情况下,我想获取 connection 节点的所有内容并创建一个连接字符串:

<connection>
            <host>
                localhost
            </host>
            <username>
                root
            </username>
            <dbName>
                Test
            </dbName>
            <dbPass>
                123456789
            </dbPass>
        </connection>

假设您不反对使用 DOMDocument 而不是 simpleXML 那么您应该可以使用以下内容。如果不是,您必须使用 simpleXML XPath 查询可以移植到 simplexml->xpath

中使用
$strxml='<?xml version="1.0" ?>
<users>
    <user>
        <name>
            foo
        </name>
        <token>
            jfhsjfhksdjfhsjkfhksjfsdk
        </token>
        <connection>
            <host>
                localhost
            </host>
            <username>
                root
            </username>
            <dbName>
                Test
            </dbName>
            <dbPass>
                123456789
            </dbPass>
        </connection>
    </user>
    <user>
        ... same structure...
    </user>
</users>';


/* an array to store details of connection */
$conndetails=array();
/* The particular value to be searched for in token nodes */
$var='jfhsjfhksdjfhsjkfhksjfsdk';


/* create the DOM objects & load xml source */
$dom=new DOMDocument;
$dom->loadXML( $strxml );
$xp=new DOMXPath( $dom );

/* Run the query to find the token with particular value - then it's next sibling */
$results=$xp->query('//token[contains( text(), "'.$var.'" )]/following-sibling::connection');

if( $results ){/* iterate through childnodes and store details in array */
    foreach( $results as $node ) {
        foreach( $node->childNodes as $child ) if( $child->nodeType==XML_ELEMENT_NODE ) $conndetails[ $child->tagName ]=$child->nodeValue;
    }
}

print_r( $conndetails );

您可以将这些选项之一变成一个函数,但您应该能够理解如何去做,看看这段代码。这将向您展示如何检索您想要的信息。

选项 1:

# using iteration
$data = new SimpleXMLElement($xml);
foreach ($data->item as $item){
    if ($item->user->token == 'jfhsjfhksdjfhsjkfhksjfsdk')
    {
        echo "host: " . $item->user->connection->host . "<br />";
        echo "username: " . $item->user->connection->username . "<br />";
        echo "dbName: " . $item->user->connection->dbName . "<br />";
        echo "dbPass: " . $item->user->connection->dbPass . "<br />";
    } else { continue; }
}

选项 2:

# using xpath
$data = new SimpleXMLElement($xml);
// Here we find the element token = jfhsjfhksdjfhsjkfhksjfsdk and get it's parent
$nodes = $data->xpath('//users/user/token[.="jfhsjfhksdjfhsjkfhksjfsdk"]/parent::*');
$user = $nodes[0];
echo "host: " . $user->connection->host . "<br />";
echo "username: " . $user->connection->username . "<br />";
echo "dbName: " . $user->connection->dbName . "<br />";
echo "dbPass: " . $user->connection->dbPass . "<br />";

首先,关于您的 XML 代码的一些注意事项。我不知道你真正的 XML 是否完全按照上面的格式,但如果是,重要的是要强调 XML 空格行为不同于 HTML:通常没有 DTD 或 XML 模式定义,所有空格都是重要的空格,应该保留。同样对于 DTD 或 XML 模式定义,内容中的空格很重要

这意味着 XML:

<token>
    jfhsjfhksdjfhsjkfhksjfsdk
</token>

<token> 假定值 '\n jfhsjfhksdjfhsjkfhksjfsdk\n'(\n = 换行符)而不是 'jfhsjfhksdjfhsjkfhksjfsdk'

这使得在不预先修改 xml 的情况下使用 xPath 很难找到令牌(您可以使用正则表达式)。您可以为此使用 contains() xPath 语法,但(理论上)它可以 return 多个结果。

我不知道你是否测试过上面的代码,还有 $value->__toString() return 类似 '\n jfhsjfhksdjfhsjkfhksjfsdk\n' 的东西(它可以根据缩进级别而改变)。要 return 只有令牌值,您必须更改您的代码:

return trim( $value->__toString() );

我的建议是无论如何改变你的XML:

<user>
    <name>foo</name>
    <token>jfhsjfhksdjfhsjkfhksjfsdk</token>
    <connection>
        <host>localhost</host>
        <username>root</username>
        <dbName>Test</dbName>
        <dbPass>123456789</dbPass>
    </connection>
</user>

因为这是最正确的数据存储方式:如果XML是自己制作的,改起来应该不会太难。

无论如何,我建议您使用两种不同的函数 - 一种带有 xPath,一种不带有。

函数 1 (xPath) 仅适用于修改后的 XML:

function getConString( $token )
{
    $data = file_get_contents($_SERVER['DOCUMENT_ROOT'] . "con");
    $xml = new SimpleXMLElement( $data );

    $path = '//users/user/token[text()="'.$token.'"]';
    $found = $xml->xpath( $path );
    if( ! count( $found ) ) return False;

    $node = $found[0]->xpath('parent::*')[0];
    return (array) $node->connection;
}

函数 2,适用于新旧 XML:

function getConString( $token )
{
    $data = file_get_contents($_SERVER['DOCUMENT_ROOT'] . "con");
    $xml = new SimpleXMLElement( $data );

    foreach( $xml->user as $user )
    {
        if( trim($user->token->__toString()) == $token )
        {
            $retval = array();
            foreach( $user->connection[0] as $key => $val )
            { $retval[$key] = trim($val); }
            return $retval;
        }
    }

    return False;
}

编辑:

在评论中,您问:“是否可以将主机,dbName等连接节点......分开并保存在特定变量中”?

我不明白他们创建这个语法有什么问题:

$user = getConString( 'jfhsjfhksdjfhsjkfhksjfsdk' );
mysqli_connect ( $user['host'], $user['username'], $user['dbPass'], $user['dbName'] );

顺便说一句,您可以通过这种方式将 returned 值放在单独的变量中:

$user = getConString( 'jfhsjfhksdjfhsjkfhksjfsdk' );
foreach( $user as $key => $val ) $$key = $val;
mysqli_connect ( $host, $username, $dbPass, $dbName );

或者 - 如果您想要特定的变量名称 - 以这种方式更改上面的第二个函数:

(...)
$retval = array();
foreach( $user->connection[0] as $key => $val )
{ $retval[] = trim($val); }
(...)

然后这样调用:

list( $dbHost, $dbUser, $dbDb, $dbPass ) = getConString( 'jfhsjfhksdjfhsjkfhksjfsdk' );
mysqli_connect ( $dbHost, $dbUser, $dbPass, $dbDb );

您可以将 list 中的变量名称更改为您喜欢的名称。函数修改是必要的,因为 list 不适用于关联数组。