在xml中获取特定节点?

时间:2016-02-06 14:57:36

标签: php simplexml

我的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>

3 个答案:

答案 0 :(得分:2)

首先,关于XML代码的一些注意事项。我不知道你的真实XML是否完全如上所述格式化,但如果是这样,重要的是要强调XML空格行为与HTML不同:通常没有DTD或XML模式定义,所有空格都是重要的空格,应该保留。此外,使用DTD或XML模式定义,内容中的空白是重要的

这意味着使用此XML:

<token>

'\n jfhsjfhksdjfhsjkfhksjfsdk\n'假定值为'jfhsjfhksdjfhsjkfhksjfsdk'(\ n =换行符),而不是contains()

这使得很难在不预先修改xml的情况下使用xPath查找令牌(您可以使用正则表达式)。您可以使用$value->__toString() xPath语法,但(理论上)它可以返回多个结果。

我不知道您是否已经测试了上面的代码,但是'\n jfhsjfhksdjfhsjkfhksjfsdk\n'还返回了类似return trim( $value->__toString() ); 的内容(它可以根据缩进级别进行更改)。要仅返回标记值,您必须更改以下代码:

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

我的建议是以这种方式改变你的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;
}

因为这是存储数据的最正确方法:如果XML是由您生成的,那么更改它应该不会太困难。

无论如何,我建议你使用两种不同的功能 - 一种是xPath,另一种是没有。

功能1(xPath)仅适用于修改后的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;
}

功能2,适用于新旧XML:

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

编辑:

在评论中,您会问:“可以将连接节点(例如主机,dbName等)分开并将其保存在特定变量中吗?”

我不明白他们可以创建这种语法的问题:

$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内的变量名称。功能修改是必要的,因为{{1}} 不适用于关联数组。

答案 1 :(得分:1)

假设您不反对使用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 );

答案 2 :(得分:1)

您可以将其中一个选项添加到函数中,但是您应该能够理解如何执行此操作,并查看此代码。这将向您展示如何检索所需的信息。

选项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 />";