php simplexml grouping objects

2019-01-27 08:31发布

问题:

Before I start I would like to say I am a noob at reading XML into PHP but I have so far managed to load XML and display the data onto a PHP page. My next test is to group items.

Here is an XML snippet:

    <MailboxDatabases>
      <MailboxDatabase>
        <DatabaseName>DB01</DatabaseName>
        <Status>Healthy</Status>
        <MailboxServer>MB08</MailboxServer>
        <ActiveDatabaseCopy>mb07</ActiveDatabaseCopy>
        <ActivationSuspended>False</ActivationSuspended>
        <SinglePageRestore>0</SinglePageRestore>
        <ContentIndexState>Healthy</ContentIndexState>
        <Active>false</Active>
      </MailboxDatabase>
      <MailboxDatabase>
        <DatabaseName>DB01</DatabaseName>
        <Status>Healthy</Status>
        <MailboxServer>MB07</MailboxServer>
        <ActiveDatabaseCopy>mb07</ActiveDatabaseCopy>
        <ActivationSuspended>False</ActivationSuspended>
        <SinglePageRestore>0</SinglePageRestore>
        <ContentIndexState>Healthy</ContentIndexState>
        <Active>true</Active>
      </MailboxDatabase>
    <MailboxDatabases>
      <MailboxDatabase>
        <DatabaseName>DB02</DatabaseName>
        <Status>Healthy</Status>
        <MailboxServer>MB08</MailboxServer>
        <ActiveDatabaseCopy>mb07</ActiveDatabaseCopy>
        <ActivationSuspended>False</ActivationSuspended>
        <SinglePageRestore>0</SinglePageRestore>
        <ContentIndexState>Healthy</ContentIndexState>
        <Active>true</Active>
      </MailboxDatabase>
      <MailboxDatabase>
        <DatabaseName>DB02</DatabaseName>
        <Status>Healthy</Status>
        <MailboxServer>MB07</MailboxServer>
        <ActiveDatabaseCopy>mb07</ActiveDatabaseCopy>
        <ActivationSuspended>False</ActivationSuspended>
        <SinglePageRestore>0</SinglePageRestore>
        <ContentIndexState>Healthy</ContentIndexState>
        <Active>false</Active>
      </MailboxDatabase>
    </MailboxDatabases>

As you can see, "DatabaseName" is the same in two items but "Active" is different. What I want to do is display the above xml in php like

DB01 - MB08 - false | DB01 - MB07 - true

DB02 - MB08 - true | DB01 - MB07 - false

using the following elements (database) - (Mailboxserver) - (active) | ...

Please could someone give me a hand and also please try to explain the code.

回答1:

Overall strategy:

  1. create a list of unique groups, that is <DatabaseName>
  2. select all <MailboxDatabase> that belong to a group and echo their <MailboxServer> and <Active>
  3. proceed to the next group and go to step 2 until you iterated over all groups.

It's time to learn about selecting certain nodes from an XML, and this is best done with xpath. In short, xpath is for XML what SQL is for databases.

Let's say you have a SimpleXML object ready:

$xml = simplexml_load_string($x); // assume XML in $x

create a list of unique groups

Get all <DatabaseName> in an array:

$groups = $xml->xpath("/MailboxDatabases/MailboxDatabase/DatabaseName");

$groups contains an array of SimpleXML elements now, so let's do some array-magic to transform those into strings:

$groups = array_map("strval", $groups);

Result (var_dump):

array(4) {
  [0]=>
  string(4) "DB01"
  [1]=>
  string(4) "DB01"
  [2]=>
  string(4) "DB02"
  [3]=>
  string(4) "DB02"
}

Some more array-magic: make it a unique list by flipping keys and values, keys must be unique, so duplicates are killed. then flip again:

$groups = array_flip(array_flip($groups));

Result:

array(2) {
  [1]=>
  string(4) "DB01"
  [3]=>
  string(4) "DB02"
}

This is it. Write it as one line:

$groups = array_flip(array_flip(array_map("strval", $xml->xpath("//MailboxDatabase/DatabaseName"));

BTW: // at the beginning of that xpath statement is a wildcard

select nodes group-wise

Again xpath, this time with a condition to select only those <MailboxDatabase> that share the same <DatabaseName>. Note the condition in []:

$elements = $xml->xpath("/MailboxDatabases/MailboxDatabase[DatabaseName = '$group']");

Now iterating over $groups:

foreach ($groups as $group) {

    echo "group $group:" . PHP_EOL;
    $elements = $xml->xpath("//MailboxDatabase[DatabaseName = '$group']");

    foreach ($elements as $e)
        echo "  " . $e->MailboxServer . ": " . $e->Active . PHP_EOL;

    echo PHP_EOL;
} 

Result:

group DB01:
  MB08: false
  MB07: true

group DB02:
  MB08: true
  MB07: false

see it working: https://eval.in/321935