PHP Convert Xml to JSON Group When There Is One Child

PHP convert XML with mixed array children to JSON keeping the child order

$xml = <<<'XML'
<root>
<a id="0"></a>
<b id="0"></b>
<b id="1"></b>
<a id="1"></a>
<a id="2"></a>
</root>
XML;

$xe = simplexml_load_string($xml);
$a = $xe->xpath('*');
$a = array_map(function ($e) {
$item = (array) $e;
$item['tag'] = $e->getName();
return $item;
}, $a);
echo json_encode($a, JSON_PRETTY_PRINT);

Output

[
{
"@attributes": {
"id": "0"
},
"tag": "a"
},
{
"@attributes": {
"id": "0"
},
"tag": "b"
},
{
"@attributes": {
"id": "1"
},
"tag": "b"
},
{
"@attributes": {
"id": "1"
},
"tag": "a"
},
{
"@attributes": {
"id": "2"
},
"tag": "a"
}
]

PHP convert XML to JSON

I figured it out. json_encode handles objects differently than strings. I cast the object to a string and it works now.

foreach($xml->children() as $state)
{
$states[]= array('state' => (string)$state->name);
}
echo json_encode($states);

PHP Converting from XML to JSON with a SimpleXML object. Array with items tag causing issues

A generic conversion has no possibility to know that a single element should be an array in JSON.

SimpleXMLElement properties can be treated as an Iterable to traverse sibling with the same name. They can be treated as an list or a single value.

This allows you to build up your own array/object structure and serialize it to JSON.

$xml = <<<'XML'
<apns>
<item>
<apn>apn1</apn>
</item>
<item>
<apn>apn2</apn>
</item>
</apns>
XML;

$apns = new SimpleXMLElement($xml);

$json = [
'apns' => []
];
foreach ($apns->item as $item) {
$json['apns'][] = ['apn' => (string)$item->apn];
}

echo json_encode($json, JSON_PRETTY_PRINT);

This still allows you to read/convert parts in a general way. Take a more in deep look at the SimpleXMLElement class. Here are method to iterate over all children or to get the name of the current node.

When converting xml to json , several objects name change to @attributes

When you json_encode() XML with attributes, this will create the @attributes elements your getting. The only way round this is to remove them as you expand them. I've changed the routine, the first thing is that I've put the part that processes the attributes first, this ensures that the root node gets processed as well.

The main thing is that I've changed the way it works to use XPath to retrieve the attributes, this then encodes them as you have, but also allows you to remove the attribute from the original node (using unset($attribute[0]);)...

function xml_expand_attributes($node)
{

foreach ($node->xpath("@*") as $attribute) {
$node->addChild($node->getName()."@".$attribute->getName(), (string)$attribute);
unset($attribute[0]);
}
if($node->count() > 0) {
foreach($node->children() as $child)
{
xml_expand_attributes($child); // 再帰呼出
}
}
}

Converting XML to JSON using PHP while preserving Arrays

I had to deal with the same situation. Here is a quick first solution to the problem - works fine for your example.

class JsonWithArrays
{
protected $root, $callback;

public function __construct( SimpleXMLElement $root )
{
$this->root = $root;
}

/**
* Set a callback to return if a node should be represented as an array
* under any circumstances.
*
* The callback receives two parameters to react to: the SimpleXMLNode in
* question, and the nesting level of that node.
*/
public function use_callback_forcing_array ( $callback )
{
$this->callback = $callback;
return $this;
}

public function to_json ()
{
$transformed = $this->transform_subnodes( $this->root, new stdClass(), 0 );
return json_encode( $transformed );
}

protected function transform_subnodes ( SimpleXMLElement $parent, stdClass $transformed_parent, $nesting_level )
{
$nesting_level++;

foreach( $parent->children() as $node )
{
$name = $node->getName();
$value = (string) $node;

if ( count( $node->children() ) > 0 )
{
$transformed_parent->$name = new stdClass();
$this->transform_subnodes( $node, $transformed_parent->$name, $nesting_level );
}
elseif ( count( $parent->$name ) > 1 or $this->force_array( $node, $nesting_level ) )
{
$transformed_parent->{$name}[] = $value;
}
else
{
$transformed_parent->$name = $value;
}
}

return $transformed_parent;
}

protected function force_array ( $node, $nesting_level )
{
if ( is_callable( $this->callback ) )
{
return call_user_func( $this->callback, $node, $nesting_level );
}
else
{
return false;
}
}
}

$xml = <<<END
<xml>
<TESTS>
<TEST>TEXT HERE</TEST>
</TESTS>
</xml>
END;

$xml2 = <<<END
<xml>
<TESTS>
<TEST>TEXT HERE</TEST>
<TEST>MORE TEXT HERE</TEST>
</TESTS>
</xml>
END;

// Callback using the node name. Could just as well be done using the nesting
// level.
function cb_testnode_as_array( SimpleXMLElement $node, $nesting_level )
{
return $node->getName() == 'TEST';
}

$transform = new JsonWithArrays( new SimpleXMLElement($xml, LIBXML_NOCDATA) );
echo $transform
->use_callback_forcing_array( 'cb_testnode_as_array' )
->to_json();

echo PHP_EOL;

$transform2 = new JsonWithArrays( new SimpleXMLElement($xml2, LIBXML_NOCDATA) );
echo $transform2
->use_callback_forcing_array( 'cb_testnode_as_array' )
->to_json();

PHP complex XML to JSON parsing

Generic conversion from XML to JSON fails for more complex XML. XML has attributes as an additional dimension, repeated nodes with the same name or mixed child nodes. JSON based formats like JsonML are possible, but that's not the JSON most people want or expect.

Try a different approach. Read values from the XML using Xpath (and DOM or SimpleXML methods). Build up the variables/data structures just as needed. You can then encode them as JSON.

$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);

$items = [];
// iterate the Table nodes
foreach ($xpath->evaluate('//NewDataSet/Table') as $tableNode) {
$items[] = [
// read CMan_Code as string
'code' => trim($xpath->evaluate('string(CMan_Code)', $tableNode)),
// read CMan_Name as string
'name' => trim($xpath->evaluate('string(CMan_Name)', $tableNode))
];
}

var_dump($items);

// encode the array variable as JSON
var_dump(json_encode($items, JSON_PRETTY_PRINT));


Related Topics



Leave a reply



Submit