使用simplexml_load_string解析XML时属性丢失问题
Ah, I’ve hit this exact problem countless times! The issue with your current simplexml_load_string() → JSON → array workflow is that SimpleXML stores XML attributes in a special @attributes key, but converting via JSON can flatten or obscure these values depending on your XML’s structure. Let’s break down the solutions:
Why Attributes Go Missing
When you run json_encode($xml), the SimpleXMLElement’s attributes get serialized into an @attributes sub-array. For simple XML, you might find them buried there, but for nested or complex structures, this can get messy or even lost entirely if the element has child nodes. For example, this XML:
<product sku="ABC123"> <name>Wireless Headphones</name> <price currency="USD">99.99</price> </product>
Would turn into an array where sku is in $array['@attributes'] and currency is in $array['price']['@attributes']—easy to miss if you’re expecting attributes to be at the same level as the element’s content.
Solution 1: Quick Fix for Simple XML
If your XML structure is straightforward, you can manually merge the @attributes into the parent array. Here’s how:
$xml = simplexml_load_string($value); $json = json_encode($xml); $array = json_decode($json, TRUE); // Recursively merge attributes into their parent elements function mergeAttributes(&$array) { if (isset($array['@attributes'])) { $array = array_merge($array['@attributes'], $array); unset($array['@attributes']); } foreach ($array as &$item) { if (is_array($item)) { mergeAttributes($item); } } } mergeAttributes($array);
This will pull all attributes up to the same level as the element’s other data, making them easier to access.
Solution 2: Robust Recursive Conversion (Best for Complex XML)
For more reliable results—especially with nested elements, repeated node names, or mixed text + attributes—use a custom recursive function to convert the SimpleXMLElement directly to an array, preserving all attributes and structure:
function xmlToArray(\SimpleXMLElement $xml, $prefixAttributes = '@') { $result = []; // Add attributes first foreach ($xml->attributes() as $attrName => $attrValue) { $result[$prefixAttributes . $attrName] = (string)$attrValue; } // Handle child nodes foreach ($xml->children() as $childName => $childNode) { // If multiple children share the same name, convert to an array if (isset($result[$childName])) { if (!is_array($result[$childName]) || !array_key_exists(0, $result[$childName])) { $result[$childName] = [$result[$childName]]; } $result[$childName][] = xmlToArray($childNode, $prefixAttributes); } else { $result[$childName] = xmlToArray($childNode, $prefixAttributes); } } // Add text content if the node has it (and isn't just empty whitespace) $textContent = trim((string)$xml); if (!empty($textContent)) { if (empty($result)) { // Node is just text, return directly return $textContent; } else { // Node has both text and children/attributes $result['#text'] = $textContent; } } return $result; } // Usage example $xml = simplexml_load_string($value); $array = xmlToArray($xml);
This function:
- Preserves all attributes (you can customize the prefix or remove it entirely by changing
$prefixAttributes) - Handles repeated child nodes by converting them into indexed arrays
- Keeps text content intact, even when mixed with attributes or child nodes
Final Notes
Avoid the JSON middleman if you can—it’s a shortcut that sacrifices structure integrity. The recursive function approach is far more reliable for real-world XML scenarios.
内容的提问来源于stack exchange,提问作者Drupal Dev

