Menu

#9 More than three elements at same level causes an error

open
nobody
None
5
2004-05-23
2004-05-23
Achton
No

I have the following XML:
---
<doc>
<section>
<para>
<heading>head1</heading>
</para>
<para>
<heading>head2</heading>
</para>
<para>
<heading>head3</heading>
</para>
<para>
<heading>head4</heading>
</para>
</section>
</doc>
---

Assuming this is available in the variable $xml, I
issue the following PHP in my script:

---
$parsedDoc = new MiniXMLDoc();
$parsedDoc->fromString($xml);
$ar = $parsedDoc->toArray();
echo "<pre>";
print_r($ar);
echo "</pre>";
---

Which wrongly returns the following array:

---
Array
(
[doc] => Array
(
[section] => Array
(
[para] => Array
(
[heading] => head4
)
)
)
)

---

Removin the fourth <para> element from the XML,
everything looks fine. What gives?

/geddeth

Discussion

  • Bill Tuttle

    Bill Tuttle - 2004-07-03

    Logged In: YES
    user_id=1076093

    Hi - we ran into the same bug working on a shopping cart. I
    spent the evening tracing and debugging and believe I have
    the fix. I have not done extensive testing, but from what
    I've tried it seems to work.

    In the file element.inc.php, in the function toStructure(),
    there is a line that reads "$existing = NULL;". I took this
    initialization and moved it to just before the enclosing
    loop and it seems to solve the problem and continue to
    funtion otherwise.

    OLD CODE:
    for($i=0; $i< $this->xnumChildren; $i++)
    {
    if ($this->isElement($this->xchildren[$i]))
    {
    $name = $this->xchildren[$i]->name();

    $struct = $this->xchildren[$i]->toStructure();
    $existing = NULL; // <= Problem Line
    if (isset($retHash[$name]))
    =========================
    NEW CODE:
    $existing = NULL; // moved from below v
    for($i=0; $i< $this->xnumChildren; $i++)
    {
    if ($this->isElement($this->xchildren[$i]))
    {
    $name = $this->xchildren[$i]->name();

    $struct = $this->xchildren[$i]->toStructure();
    //$existing = NULL; // Moved to before the loop ^
    if (isset($retHash[$name]))
    =========================

    Hope this helps!

     
  • Simon Cocking

    Simon Cocking - 2004-11-16

    Logged In: YES
    user_id=1159949

    Hi,

    I believe the fix suggested by gryphonsmoon is incorrect. I
    was still able to hit a bug with that fix in place, with the
    following XML:

    <parent>
    <child1>somevalue</child1>
    <child1>anothervalue</child1>
    <child1>yetanothervalue</child1>
    <child2>something</child2>
    <child2>somethingelse</child2>
    <child2>anothersomething</child2>
    </parent>

    Basically, the resulting array had child2's values in the
    child1 array.

    The real bug is that the author has made the assumption that
    assigning an empty array to an existing array variable will
    "clear" that array. Unfortunately PHP4 doesn't work that
    way -- you have to unset() the variable first.

    I had cleaned up the toStructure() method before I realised
    that this one-liner would fix the bug; my cleanup diff follows:

    --- orig_element.inc.php 2004-11-16 12:28:05.000000000 +1100
    +++ element.inc.php 2004-11-16 12:28:05.000000000 +1100
    @@ -1237,25 +1237,20 @@
    {
    $name = $this->xchildren[$i]->name();
    $struct = $this->xchildren[$i]->toStructure();
    - $existing = NULL;
    - if (isset($retHash[$name]))
    - {
    - $existing =& $retHash[$name];
    - }
    -

    - if ($existing)
    - {
    - if (_MiniXML_NumKeyArray($existing))
    - {
    - array_push($existing, $struct);
    + if (isset($retHash[$name])) {
    + // A child element with this name has already been seen
    + if (_MiniXML_NumKeyArray($retHash[$name])) {
    + // The child element array was already a plain array, so
    + // we can just append this instance to it
    + array_push($retHash[$name], $struct);
    } else {
    - $newArray = array();
    - array_push($newArray, $existing);
    - array_push($newArray, $struct);
    - $retHash[$name] =& $newArray;
    + // The child element array was associative, so in order
    + // to append this instance we need to convert it to a
    plain array
    + $retHash[$name] = array($retHash[$name], $struct);
    }
    } else {
    + // We've not seen this child element before
    $retHash[$name] = $struct;
    }
    $numAdded++;

    Hope this helps!

    Simon.

     

Log in to post a comment.