Asked  1 Year ago    Answers:  5   Viewed   16 times

The following test function reads an XML file with PHP's xmlreader and returns a 2 dimensional array of 3 values ("id" = key).

How can you edit the code so that the username key and value is returned in the array also? (shown at the end)

ini_set('always_populate_raw_post_data', 'on');

function test()
{
    $request = $HTTP_RAW_POST_DATA;
    error_reporting(E_ERROR | E_WARNING | E_PARSE);
    $url = "http://site.xml";
    $reader = new XMLReader();
    $reader->open($url);;

    $var = array();
    $i = 0;
    $limit = 3;

    while ($reader->read()) 
    {
        if ($reader->name == "id" && $reader->nodeType == XMLReader::ELEMENT)
        {
            if ($i == $limit) break;
            while ($reader->read())
            {
                if ($reader->nodeType == XMLReader::TEXT
                    || $reader->nodeType == XMLReader::CDATA
                    || $reader->nodeType == XMLReader::WHITESPACE
                    || $reader->nodeType == XMLReader::SIGNIFICANT_WHITESPACE)
                {
                    $var[$i]["id"] = $reader->value;
                }
                else if ($reader->nodeType == XMLReader::END_ELEMENT && $reader->name == "id")
                {
                    break;
                }
             }
             $i++;
        }
    }
    $reader->close();

    echo '<pre>';
    print_r($var);
    echo '</pre>';
}

test() returns this array

Array
(
    [0] => Array
        (
            [id] => 345
        )

    [1] => Array
        (
            [id] => 123
        )

    [2] => Array
        (
            [id] => 789
        )
)

How do you also get username key and value?

Array
(
    [0] => Array
        (
            [id] => 345
            [username] => name1
        )

    [1] => Array
        (
            [id] => 123
            [username] => name2
        )

    [2] => Array
        (
            [id] => 789
            [username] => name3       
        )
)

Add to Jose's answer

// needed to end element
else if ($reader->nodeType == XMLReader::END_ELEMENT && $reader->name == "username")
{
    break;
}

 Answers

5

This should work.

function test()
{
    $request = $HTTP_RAW_POST_DATA;
    error_reporting(E_ERROR | E_WARNING | E_PARSE);
    $url = "http://site.xml";
    $reader = new XMLReader();
    $reader->open($url);;

    $var = array();
    $i = 0;
    $limit = 3;

    while ($reader->read()) 
    {
        if (($reader->name == "id" || $reader->name == "username") && $reader->nodeType == XMLReader::ELEMENT)
        {
        $name = $reader->name;
            if ($i == $limit) break;
            while ($reader->read())
            {
                if ($reader->nodeType == XMLReader::TEXT
                    || $reader->nodeType == XMLReader::CDATA
                    || $reader->nodeType == XMLReader::WHITESPACE
                    || $reader->nodeType == XMLReader::SIGNIFICANT_WHITESPACE)
                {
                    $var[$i][$name] = $reader->value;
                }
                else if ($reader->nodeType == XMLReader::END_ELEMENT && $reader->name == "id")
                {
                    break;
                }
             }

             if($name == "username")
        $i++;

        }
    }
    $reader->close();

    echo '<pre>';
    print_r($var);
    echo '</pre>';
}

CHANGES:

($reader->name == "id" || $reader->name == "username")

$name = $reader->name;

$var[$i][$name] = $reader->value;

if($name == "username") $i++;

Saturday, May 29, 2021
 
mopsyd
 
1

If I am understanding this correctly:

Returning a new array:

$array_new = [];
foreach($array_two as $key)
{
    if(array_key_exists($key, $array_one))
    {
        $array_new[$key] = $array_one[$key];
    }
}

Stripping from $array_one:

foreach($array_one as $key => $val)
{
    if(array_search($key, $array_two) === false)
    {
        unset($array_one[$key]);
    }
}
Thursday, April 1, 2021
 
PHLAK
 
1

I cannot add a comment, but I think this will work for you, it should be faster then a regex or a loop:

//after you json_encode, before you decode
$str = str_replace(':[]',':null',json_encode($array));

An empty array in JSON is represented by "[]". Sometimes the arrays are parsed as objects, in that case (or as a fallback) you can replace ":{}" too.

Saturday, May 29, 2021
 
5

Looks like there is no way to suppress the warning other than to use @, since php source for read() has following lines:

retval = xmlTextReaderRead(intern->ptr);
if (retval == -1) {
    php_error_docref(NULL TSRMLS_CC, E_WARNING, "An Error Occured while reading");
    RETURN_FALSE;
} else {
    RETURN_BOOL(retval);
}

So, only the actual parsing errors inside xmlTextReaderRead() are being suppressed by the libxml_use_internal_errors(true); or the options passed to XMLReader::xml().

Saturday, May 29, 2021
 
Octopus
 
5

Add a break statement after the end of the first switch condition on the nodeType:

<?php
$xml = new XMLReader();
$xml->open("php://stdin");

while($xml->read()) {

  switch($xml->nodeType) {
    case XMLReader::ELEMENT:
      switch($xml->name) {
        case 'author':
          echo("+" . $xml->name);
          break;
    }

    // THIS LINE IS MISSING
    break;

    case XMLReader::END_ELEMENT:
      switch($xml->name) {
        case 'author':
          echo("-" . $xml->name);
          break;
      }
    }
  }
?>

Add another break after reading the END_ELEMENT, as well, if only for symmetry.

    case XMLReader::END_ELEMENT:
      switch($xml->name) {
        case 'author':
          echo("-" . $xml->name);
          break;
      }
    }

    break;

The problem happened because of the coding style. Simplify the code. For example:

$xml = new XMLReader();
$xml->open("php://stdin");

while($xml->read()) {    
  switch($xml->nodeType) {
    case XMLReader::ELEMENT: {
      startElement( $xml->name );
      break;
    }

    case XMLReader::END_ELEMENT: {
      endElement( $xml->name );
      break;
    }
  }
}

There are further simplifications you can make. PHP has an XML marshalling package, but you could also abstract the code into classes. Instances of those classes would then be able to read (or write) themselves from (or to) an XML file. For example:

$xml = new XMLReader();
$xml->open("php://stdin");

while($xml->read()) {    
  if( $xml->name == 'author' ) {
    $author = new Author();
    $author->marshall( $xml );
  }
}

This couples the details of how the object is stored with the object itself. Any time you change the Author object, you know you must change how it marshalls itself. You could abstract and extend these concepts even further using appropriate design patterns, XML schemas, and so forth.

Thus your final code might resemble:

$xml = new XMLReader();
$xml->open( "php://stdin" );
$publications = new Publications();
$publications->marshall( $xml );

The Publications object is responsible for reading the XML document and instantiating the appropriate classes whenever their associated XML tags appear:

while($xml->read()) {    
  $article = new Article();
  $article->marshall( $xml );
  add( $article );
}

Use a PHP marshalling framework to save yourself time and effort. Consider XML_Serializer:

  • http://pear.php.net/package/XML_Serializer
Saturday, May 29, 2021
 
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :