SimpleXML xpath within selected node

2019-03-04 00:11发布

问题:

I have the following XML file

<Item> 
  <name>...</name>
  <id>...</id>   
  <ImageSets>    
    <ImageSet Category="variant">
      <SwatchImage>
        <URL>...</URL>
        <Height Units="pixels"></Height>
        <Width Units="pixels"></Width>
      </SwatchImage>
      <SmallImage>
        <URL>...</URL>
        <Height Units="pixels"></Height>
        <Width Units="pixels"></Width>
      </SmallImage>
    </ImageSet>

    <ImageSet Category="primary">
      <SwatchImage>
        <URL>...</URL>
        <Height Units="pixels"></Height>
        <Width Units="pixels"></Width>
      </SwatchImage>
      <SmallImage>
        <URL>...</URL>
        <Height Units="pixels"></Height>
        <Width Units="pixels"></Width>
      </SmallImage>
    </ImageSet>
  </ImageSets>
</Item>

<Item>....</Item>

Then I use PHP to iterate through Item nodes within file.

foreach ($Xml as $item){
    $name = $item->name;
    $ID = $item->id;
};

And so on. The code works perfect extracting names and Id's for every item.

Now my problem is to extract ImageSet Category='primary'->SmallImage->URL. ImageSet nodes do not go in any particular order, so sometimes 'primary' will be first, sometimes 'variant', that's why $item->ImageSets->ImageSet[1] is not a solution

So within my main foreach loop I've tried using xpath as follows:

$src='';    
foreach ($item->ImageSets->xpath('//ImageSet[@Category="primary"]') as $img){
    $src = $img->MediumImage->URL;
};

With absolutely no luck.

Any ideas would be appreciated.

回答1:

Realtive to your context node $item (I have no doubt there's why you need that context node ;-)) you're looking for ImageSet that a) is a child of ImageSets (which in turn is a direct child of your context node) and b) has the attribute Category with the value primary (which you have coded correctly)

<?php
$itemset = new SimpleXMLElement(data());
foreach ($itemset as $item) { // or something else - just some reason why you have to work with $item
    foreach ($item->xpath('ImageSets/ImageSet[@Category="primary"]') as $p) {
        echo $p->SwatchImage->URL;
    }
}


function data() {
    return <<< eox
<ItemSet>
    <Item>
      <name>...</name>
      <id>...</id>
      <ImageSets>
        <ImageSet Category="variant">
          <SwatchImage>
            <URL>...</URL>
            <Height Units="pixels"></Height>
            <Width Units="pixels"></Width>
          </SwatchImage>
          <SmallImage>
            <URL>...</URL>
            <Height Units="pixels"></Height>
            <Width Units="pixels"></Width>
          </SmallImage>
        </ImageSet>
        <ImageSet Category="primary">
          <SwatchImage>
            <URL>primary swatch image url</URL>
            <Height Units="pixels"></Height>
            <Width Units="pixels"></Width>
          </SwatchImage>
          <SmallImage>
            <URL>...</URL>
            <Height Units="pixels"></Height>
            <Width Units="pixels"></Width>
          </SmallImage>
        </ImageSet>
      </ImageSets>
    </Item>
    <Item>...</Item>
</ItemSet>      
eox;
}


回答2:

You have an error in the XPath expression. If the expression starts with an / it is relative to the document itself. You want it relative to the current node. This means you have two possible solutions

ImageSet[@Category="primary"]

This expands to child::ImageSet. It fetches the ImageSet element nodes that are direct children of the context node.

.//ImageSet[@Category="primary"]

Expands and normalizes to descendant::ImageSet. It fetches any ImageSet inside the current context, even if it is not a direct child. The . represents the current node and // changes the axis to descendants.