Retrieving a subset of XML nodes with PHP

2019-01-27 00:10发布

问题:

Using PHP, how do I get an entire subset of nodes from an XML document? I can retrieve something like:

<?xml version="1.0" encoding="utf-8"?>
<people>
  <certain>
    <name>Jane Doe</name>
    <age>21</age>
  </certain>
  <certain>
  <certain>
    <name>John Smith</name>
    <age>34</age>
  </certain>
</people>

But what if I only want to return the child nodes of like this?

  <certain>
    <name>Jane Doe</name>
    <age>21</age>
  </certain>
  <certain>
  <certain>
    <name>John Smith</name>
    <age>34</age>
  </certain>

EDIT: I'm trying to get a subset of XML and pass that directly, not an object like simplexml would give me. I am basically trying to get PHP to do what .NET's OuterXml does... return literally the above subset of XML as is... no interpreting or converting or creating a new XML file or anything... just extract those nodes in situ and pass them on. Am I going to have to get the XML file, parse out what I need and then rebuild it as a new XML file? If so then I need to get rid of the <?xml version="1.0" encoding="utf-8"?> bit... ugh.

回答1:

The answer would be to use XPath.

$people = simplexml_load_string(
    '<?xml version="1.0" encoding="utf-8"?>
    <people>
      <certain>
        <name>Jane Doe</name>
        <age>21</age>
      </certain>
      <certain>
        <name>John Smith</name>
        <age>34</age>
      </certain>
    </people>'
);

// get all <certain/> nodes
$people->xpath('//certain');

// get all <certain/> nodes whose <name/> is "John Smith"
print_r($people->xpath('//certain[name = "John Smith"]'));

// get all <certain/> nodes whose <age/> child's value is greater than 21
print_r($people->xpath('//certain[age > 21]'));

Take 2

So apparently you want to copy some nodes from a document into another document? SimpleXML doesn't support that. DOM has methods for that but they're kind of annoying to use. Which one are you using? Here's what I use: SimpleDOM. In fact, it's really SimpleXML augmented with DOM's methods.

include 'SimpleDOM.php';
$results = simpledom_load_string('<results/>');

foreach ($people->xpath('//certain') as $certain)
{
    $results->appendChild($certain);
}

That routine finds all <certain/> node via XPath, then appends them to the new document.



回答2:

You could use DOMDocument.GetElementsByTagName or you could:

Use XPath?

<?php
$xml = simplexml_load_file("test.xml");

$result = $xml->xpath("//certain");

print_r($result);
?>


回答3:

Use DOM and XPath. Xpath allows you to select nodes (and values) from an XML DOM.

$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);

$result = '';
foreach ($xpath->evaluate('/people/certain') as $node) {
  $result .= $dom->saveXml($node);
}

echo $result;

Demo: https://eval.in/162149

DOMDocument::saveXml() has a context argument. If provided it saves that node as XML. Much like outerXml(). PHP is able to register your own classes for the DOM nodes, too. So it is even possible to add an outerXML() function to element nodes.

class MyDomElement extends DOMElement {
  public function outerXml() {
    return $this->ownerDocument->saveXml($this);
  }
}

class MyDomDocument extends DOMDocument {
  public function __construct($version = '1.0', $encoding = 'utf-8') {
    parent::__construct($version, $encoding);
    $this->registerNodeClass('DOMElement', 'MyDomElement');
  }
}

$dom = new MyDomDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);

$result = '';
foreach ($xpath->evaluate('/people/certain') as $node) {
  $result .= $node->outerXml();
}

echo $result;

Demo: https://eval.in/162157



回答4:

See http://www.php.net/manual/en/domdocument.getelementsbytagname.php



回答5:

The answer turned out to be a combination of the xpath suggestion and outputting with asXML().

Using the example given by Josh Davis:

$people = simplexml_load_string(
      <?xml version="1.0" encoding="utf-8"?>
        <people>
          <certain>
            <name>Jane Doe</name>
            <age>21</age>
          </certain>
          <certain>
            <name>John Smith</name>
            <age>34</age>
          </certain>
        </people>'
    );

    // get all <certain/> nodes
    $nodes = $people->xpath('/people/certain');

    foreach ( $nodes as $node ) {
      $result .= $node->asXML()."\n";
    }
    echo $result;