Search code examples
phparrayssimplexml

How get the value of element $item["@attributes"]["tag"]


How can I call to $item["@attributes"]["tag"]?
I receive the Fatal error: Cannot use string offset as an array in line:

if ($item["@attributes"]["tag"] == $field)

My steps: I receiving XML string, extract it to SimpleXMLObject:

object(SimpleXMLElement)#6 (1) 
{ 
["rec"]=> object(SimpleXMLElement)#7 (1) 
  { 
    ["datafield"]=> array(1) { 
    [0]=> object(SimpleXMLElement)#10 (3) 
       {
        ["@attributes"]=> array(1) { 
        ["tag"]=> string(2) "99" 
        }   
        ["@attributes"]=> array(1) { 
        ["tag"]=> string(3) "100" 
        } 

        ["subfield"]=> array(3) { 
         [0]=> string(5) "text0" 
         [1]=> string(5) "text1" 
         [2]=> string(4) "test" 
        } 
       } 
       } 
  } 
} 

Because I have no attribute [code] in SimpleXMLObject I convert it to array, to find the [tag]="100" and change ["code"] or if code=z not exist create it and insert value that I should change or create:

array(1) {
  ["hold"]=> array(1) { 
     ["rec"]=> array(1){ 
      ["datafield"]=> array(2) { 
      [0]=> array(2) 
         { 
          ["subfield"]=> array(1) { 
           [0]=> array(2) { 
              ["@value"]=> string(4) "test" 
              ["@attributes"]=> array(1){ 
              ["code"]=> string(1) "z" 
              } 
             } 
         } 
           ["@attributes"]=> array(1) { 
             ["tag"]=> string(3) "100" 
            } 
          } 
        } 
     } 
 } 
} 

This is my PHP code to access the array:

foreach ($xml_ray['hold']['rec'] as $key_item => $item) {
   if ($item["@attributes"]["tag"] == $field) { // Line where I get the ERROR
      foreach ($item['subfield'] as $key_subfield => $subfield) {
          $code = $subfield['@attributes']['code'];
          $value = $subfield['@value'];
               //  ....       
     if ($checksum==2 && $code==$subfield_file) {
           $checksum++;
          }

        if ($checksum == 3) { $xml_hold_ray['hold']['rec'][$key_item]['subfield'][$key_subfield]['@value'] = $new_code;
          }
            }

         }
      }

The Origin XML:

  <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <hold>
    <rec>
      <datafield ind1=" " ind2=" " tag="99">
       <subfield code="a">text</subfield>
      </datafield>
      <datafield ind1="1" ind2=" " tag="100">
        <subfield code="b">text0</subfield>
        <subfield code="c">text1</subfield>
        <subfield code="z">test2</subfield>
      </datafield>
    </rec>
    </hold>

The whole code for Understand the work of it:

 foreach ($xml_ray['hold']['rec'] as $key_item => $item) {

      if ($item["@attributes"]["tag"] == $field) {
  $info_msg = "FIELD: $field <br />";
    $outcame_msg .="Found correct field $field<br />";
  foreach ($item['subfield'] as $key_subfield => $subfield) {
      $code = $subfield['@attributes']['code'];
    $value = $subfield['@value'];
      $info_holding_msg.="$code = $value<br />";

      if ($value==$li) {
    $outcame_msg .= "Found correct li in subfield $code<br />";
    $checksum++;
    }
      if ($value==$loc) {
    $outcame_msg .= "Found correct loc in subfield $code<br />";
    $checksum++;
    }

       if ($checksum==2 && $code==$subfield_file) {

       $outcame_msg .= "Found subfield $code with value: $value<br />";
       $checksum++;
      }

    if ($checksum == 3) {

    $xml_ray['hold']['rec'][$key_item]['subfield'][$key_subfield]['@value'] = $new_code;
    $outcame_msg.="data found, changing data accepted.";
      }           
        }         
     }
   }

Update new XML:

$data ='<holds total="4">
<hold link="#">
<hold_id>0000000000000000</hold_id>
<lib desc="Lib">text</lib>
<loc desc="Lib2">text1</loc>
<sup>false</sup>
</hold>
<hold link="#">
<hold_id>0000000000000000</hold_id>
<lib desc="Lib">text</lib>
<loc desc="Lib2">text1</loc>
<sup>false</sup>
</hold>
<hold link="#">
<hold_id>0000000000000000</hold_id>
<lib desc="Lib">text3</lib>
<loc desc="Lib2">text4</loc>
<sup>false</sup>
</hold>   
</holds>';

Solution

  • Using SimpleXML, this is how to access the data

    $data = <<< XML
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
    <hold> 
    <rec>
      <datafield ind1=" " ind2=" " tag="99">
       <subfield code="a">text</subfield>
      </datafield>
      <datafield ind1="1" ind2=" " tag="100">
        <subfield code="b">text0</subfield>
        <subfield code="c">text1</subfield>
        <subfield code="z">test2</subfield>
      </datafield>
    </rec>
    </hold>
    XML;
    
    $xml_ray = simplexml_load_string($data);
    
    // A test value
    $field = "100";
    $checksum=2;
    $new_code = 't1';
    $subfield_file="z";
    
    // Loop through all the <datafield> elements
    foreach ( $xml_ray->rec->datafield as $item ) {
        if ( $item['tag'] == $field )    {
            $replaced = false;
            foreach ($item->subfield as $subfield) {
                if ($subfield['code']==$subfield_file) {
                    $subfield[0] = $new_code;
                    $replaced = true;
                }
            }
            if ( $replaced == false )   {
                $newItem = $item->addChild("subfield", $new_code);
                $newItem->addAttribute("code", $subfield_file);
            }
        }
    }
    

    See the way you use ->rec to access the sub elements and ['tag'] to access the attributes.

    I'm not 100% sure about what your doing with the middle bit of code, but I've tried to add in what I think your doing. This code doesn't add it in if it doesn't exist and I'm not sure how the $checksum field processing works.

    Two examples of how to use XPath...

    $match = $xml_ray->xpath("//datafield[subfield[@code='z']]");
    if ( count($match) > 0 )    {
        echo "Found match";
    }
    else    {
        echo "No match found";
    }
    $match = $xml_ray->xpath("//datafield[@tag='100'][subfield[@code='z1']]");
    if ( count($match) > 0 )    {
        echo "Found match";
    }
    else    {
        echo "No match found";
    }
    

    The first (//datafield[subfield[@code='z']]) matches any datafield element with a subfield which has an attrbute code with a value of 'z'. This finds an element so prints 'Found Match'.

    The second (//datafield[@tag='100'][subfield[@code='z1']] ) matches any datafield element which has a tag attribute = 100 and a subfield with code = 'z1'. This doesn't find an element so prints 'No match found'.

    Update: If you want to select the value of the datafield element with attribute tag = 100 and sibfield with code="z", then...

    $match = $xml_ray->xpath("//datafield[@tag='100']/subfield[@code='z']");
    echo (string)$match[0];
    

    I've changed the XPath slightly from before, but this XPath gives...

    test2