Home » Php » xml – Disappearing attributes in PHP SimpleXML Object?

xml – Disappearing attributes in PHP SimpleXML Object?

Posted by: admin July 12, 2020 Leave a comment

Questions:

I need to return a SimpleXML object converted as a JSON object to work with it in JavaScript. The problem is that there are no attributes on any object with a value.

As an example:

<customer editable="true" maxChars="9" valueType="numeric">69236</customer>

becomes in the SimpleXML object:

"customer":"69236"

Where is the @attributes object?

How to&Answers:

This has driven me crazy on several occasions. When SimpleXML encounters a node that only has a text value, it drops all the attributes. My workaround has been to modify the XML prior to parsing with SimpleXML. With a bit of regular expressions, you can create a child node that contains the actual text value. For example, in your situation you can change the XML to:

<customer editable="true" maxChars="9" valueType="numeric"><value>69236<value></customer>

Some example code assuming that your XML string was in $str:

$str = preg_replace('/<customer ([^>]*)>([^<>]*)<\/customer>/i', '<customer $1><value>$2</value></customer>', $str);
$xml = @simplexml_load_string($str);

That would preserve the attributes and nest the text value in a child node.

Answer:

I realize this is an old post, but in case it proves useful. The below extends @ryanmcdonnell’s solution to work on any tags instead of a hard-coded tag. Hopefully it helps someone.

$str = preg_replace('/<([^ ]+) ([^>]*)>([^<>]*)<\/\1>/i', '<$1 $2><value>$3</value></$1>', $result);

The main different is that it replaces /<customer with /<([^ ]+), and then </customer> with </\\1>

which tells it to match that part of the search against the first element in the pattern.

Then it just adjusts the placeholders ($1,$2,$3) to account for the fact that there are three sub-matches now instead of two.

Answer:

So it appears that this is a bug and is fixed in PHP 7.4.5.

Answer:

It’s an old question, but I found something that works neat – parse it into a DOMNode object.

// $customer contains the SimpleXMLElement
$customerDom = dom_import_simplexml($customer);
var_dump($customerDom->getAttribute('numeric'));

Will show:

string 'numeric'

Answer:

Here’s some code to iterate through attributes, and construct JSON. If supports, one or many customers.

If you’re XML looks like this (or just one customer)

<xml>
<customer editable="true" maxChars="9" valueType="numeric">69236</customer>
<customer editable="true" maxChars="9" valueType="numeric">12345</customer>
<customer editable="true" maxChars="9" valueType="numeric">67890</customer>
</xml>

Iterate through it like this.

try {
    $xml = simplexml_load_file( "customer.xml" );

    // Find the customer
    $result = $xml->xpath('/xml/customer');

    $bFirstElement = true;
    echo     "var customers  = {\r\n";
    while(list( , $node) = each($result)) {
        if( $bFirstElement ) {
            echo "'". $node."':{\r\n";
            $bFirstElement = false;
        } else {
            echo ",\r\n'". $node."':{\r\n";
        }

        $bFirstAtt = true;
        foreach($node->attributes() as $a => $b) { 
            if( $bFirstAtt ) {
                echo "\t".$a.":'".$b."'";
                $bFirstAtt = false;
            } else {
                echo ",\r\n\t".$a.":'".$b."'";
            }
        }
        echo "}";
    }
    echo "\r\n};\r\n";
} catch( Exception $e ) {
    echo "Exception on line ".$e->getLine()." of file ".$e->getFile()." : ".$e->getMessage()."<br/>";
}

To produce a JSON structure like this

var customers  = {
'69236':{
    editable:'true',
    maxChars:'9',
    valueType:'numeric'},
'12345':{
    editable:'true',
    maxChars:'9',
    valueType:'numeric'},
'67890':{
    editable:'true',
    maxChars:'9',
    valueType:'numeric'}
};

Finally, in your script, access the attribute like this

WScript.Echo( customers["12345"].editable );

Good luck