PHP convertir XML en JSON
J'essaie de convertir xml en json en php. Si je fais une conversion simple en utilisant xml simple et json_encode aucun des attributs dans le spectacle xml.
$xml = simplexml_load_file("states.xml");
echo json_encode($xml);
J'essaie donc de l'analyser manuellement comme ceci.
foreach($xml->children() as $state)
{
$states[]= array('state' => $state->name);
}
echo json_encode($states);
Et la sortie pour l'état est {"state":{"0":"Alabama"}}
plutôt que {"state":"Alabama"}
Ce que je fais mal?
XML:
<?xml version="1.0" ?>
<states>
<state id="AL">
<name>Alabama</name>
</state>
<state id="AK">
<name>Alaska</name>
</state>
</states>
Sortie:
[{"state":{"0":"Alabama"}},{"state":{"0":"Alaska"}
VAR dump:
object(SimpleXMLElement)#1 (1) {
["state"]=>
array(2) {
[0]=>
object(SimpleXMLElement)#3 (2) {
["@attributes"]=>
array(1) {
["id"]=>
string(2) "AL"
}
["name"]=>
string(7) "Alabama"
}
[1]=>
object(SimpleXMLElement)#2 (2) {
["@attributes"]=>
array(1) {
["id"]=>
string(2) "AK"
}
["name"]=>
string(6) "Alaska"
}
}
}
16 réponses
JSON & Array à partir de XML en 3 lignes:
$xml = simplexml_load_string($xml_string);
$json = json_encode($xml);
$array = json_decode($json,TRUE);
Ta da!
Désolé de répondre à un ancien post, mais cet article décrit une approche relativement courte, concise et facile à maintenir. Je l'ai testé moi-même et fonctionne assez bien.
Http://lostechies.com/seanbiefeld/2011/10/21/simple-xml-to-json-with-php/
<?php
class XmlToJson {
public function Parse ($url) {
$fileContents= file_get_contents($url);
$fileContents = str_replace(array("\n", "\r", "\t"), '', $fileContents);
$fileContents = trim(str_replace('"', "'", $fileContents));
$simpleXml = simplexml_load_string($fileContents);
$json = json_encode($simpleXml);
return $json;
}
}
?>
J'ai compris. json_encode gère les objets différemment des chaînes. Je lance l'objet dans une chaîne et cela fonctionne maintenant.
foreach($xml->children() as $state)
{
$states[]= array('state' => (string)$state->name);
}
echo json_encode($states);
Je suppose que je suis un peu en retard à la fête mais j'ai écrit une petite fonction pour accomplir cette tâche. Il prend également en charge les attributs, le contenu du texte et même si plusieurs nœuds avec le même nom de nœud sont frères et sœurs.
Dislaimer: Je ne suis pas un natif de PHP, alors veuillez supporter des erreurs simples.
function xml2js($xmlnode) {
$root = (func_num_args() > 1 ? false : true);
$jsnode = array();
if (!$root) {
if (count($xmlnode->attributes()) > 0){
$jsnode["$"] = array();
foreach($xmlnode->attributes() as $key => $value)
$jsnode["$"][$key] = (string)$value;
}
$textcontent = trim((string)$xmlnode);
if (count($textcontent) > 0)
$jsnode["_"] = $textcontent;
foreach ($xmlnode->children() as $childxmlnode) {
$childname = $childxmlnode->getName();
if (!array_key_exists($childname, $jsnode))
$jsnode[$childname] = array();
array_push($jsnode[$childname], xml2js($childxmlnode, true));
}
return $jsnode;
} else {
$nodename = $xmlnode->getName();
$jsnode[$nodename] = array();
array_push($jsnode[$nodename], xml2js($xmlnode, true));
return json_encode($jsnode);
}
}
Exemple d'Utilisation:
$xml = simplexml_load_file("myfile.xml");
echo xml2js($xml);
Exemple d'Entrée (myfile.xml):
<family name="Johnson">
<child name="John" age="5">
<toy status="old">Trooper</toy>
<toy status="old">Ultrablock</toy>
<toy status="new">Bike</toy>
</child>
</family>
Exemple de sortie:
{"family":[{"$":{"name":"Johnson"},"child":[{"$":{"name":"John","age":"5"},"toy":[{"$":{"status":"old"},"_":"Trooper"},{"$":{"status":"old"},"_":"Ultrablock"},{"$":{"status":"new"},"_":"Bike"}]}]}]}
Joli imprimé:
{
"family" : [{
"$" : {
"name" : "Johnson"
},
"child" : [{
"$" : {
"name" : "John",
"age" : "5"
},
"toy" : [{
"$" : {
"status" : "old"
},
"_" : "Trooper"
}, {
"$" : {
"status" : "old"
},
"_" : "Ultrablock"
}, {
"$" : {
"status" : "new"
},
"_" : "Bike"
}
]
}
]
}
]
}
Bizarreries de garder à l'esprit: Plusieurs tags avec le même tagname peuvent être frères et sœurs. D'autres solutions laisseront probablement tomber tous sauf le dernier frère. Pour éviter cela, chaque nœud, même s'il n'a qu'un seul enfant, est un tableau qui contient un objet pour chaque instance du tagname. (Voir plusieurs "éléments" dans l'exemple)
Même l'élément racine, dont un seul devrait exister dans un document XML valide, est stocké en tant que tableau avec un objet du instance, juste pour avoir une structure de données cohérente.
Pour pouvoir faire la distinction entre le contenu du nœud XML et les attributs XML, les attributs de chaque objet sont stockés dans le " $ "et le contenu dans l'enfant"_".
Modifier: j'ai oublié d'afficher la sortie de votre exemple de données d'entrée
{
"states" : [{
"state" : [{
"$" : {
"id" : "AL"
},
"name" : [{
"_" : "Alabama"
}
]
}, {
"$" : {
"id" : "AK"
},
"name" : [{
"_" : "Alaska"
}
]
}
]
}
]
}
Essayez d'utiliser ceci
$xml = ... // Xml file data
// first approach
$Json = json_encode(simplexml_load_string($xml));
---------------- OR -----------------------
// second approach
$Json = json_encode(simplexml_load_string($xml, "SimpleXMLElement", LIBXML_NOCDATA));
echo $Json;
Ou
Vous pouvez utiliser cette bibliothèque : https://github.com/rentpost/xml2array
Un piège commun est d'oublier que json_encode()
ne respecte pas les éléments avec un attribut textvalue et. Il choisira l'un de ceux-ci, ce qui signifie dataloss.
La fonction ci-dessous résout ce problème. Si l'on décide d'aller pour le json_encode
/decode
façon, la fonction suivante est conseillé.
function json_prepare_xml($domNode) {
foreach($domNode->childNodes as $node) {
if($node->hasChildNodes()) {
json_prepare_xml($node);
} else {
if($domNode->hasAttributes() && strlen($domNode->nodeValue)){
$domNode->setAttribute("nodeValue", $node->textContent);
$node->nodeValue = "";
}
}
}
}
$dom = new DOMDocument();
$dom->loadXML( file_get_contents($xmlfile) );
json_prepare_xml($dom);
$sxml = simplexml_load_string( $dom->saveXML() );
$json = json_decode( json_encode( $sxml ) );
Ce faisant, <foo bar="3">Lorem</foo>
ne finira pas comme {"foo":"Lorem"}
dans votre JSON.
Optimisation de la réponse Antonio Max:
$xmlfile = 'yourfile.xml';
$xmlparser = xml_parser_create();
// open a file and read data
$fp = fopen($xmlfile, 'r');
//9999999 is the length which fread stops to read.
$xmldata = fread($fp, 9999999);
// converting to XML
$xml = simplexml_load_string($xmldata, "SimpleXMLElement", LIBXML_NOCDATA);
// converting to JSON
$json = json_encode($xml);
$array = json_decode($json,TRUE);
J'ai utilisé TypeConverter de Miles Johnson à cette fin. Il est installable en utilisant Composer .
Vous pouvez écrire quelque chose comme ceci en l'utilisant:
<?php
require 'vendor/autoload.php';
use mjohnson\utility\TypeConverter;
$xml = file_get_contents("file.xml");
$arr = TypeConverter::xmlToArray($xml, TypeConverter::XML_GROUP);
echo json_encode($arr);
Ceci est une amélioration de la solution la plus votée par Antonio Max, qui fonctionne également avec XML qui a des espaces de noms (en remplaçant les deux-points par un trait de soulignement). Il a également quelques options supplémentaires (et analyse correctement <person my-attribute='name'>John</person>
).
function parse_xml_into_array($xml_string, $options = array()) {
/*
DESCRIPTION:
- parse an XML string into an array
INPUT:
- $xml_string
- $options : associative array with any of these keys:
- 'flatten_cdata' : set to true to flatten CDATA elements
- 'use_objects' : set to true to parse into objects instead of associative arrays
- 'convert_booleans' : set to true to cast string values 'true' and 'false' into booleans
OUTPUT:
- associative array
*/
// Remove namespaces by replacing ":" with "_"
if (preg_match_all("|</([\\w\\-]+):([\\w\\-]+)>|", $xml_string, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) {
$xml_string = str_replace('<'. $match[1] .':'. $match[2], '<'. $match[1] .'_'. $match[2], $xml_string);
$xml_string = str_replace('</'. $match[1] .':'. $match[2], '</'. $match[1] .'_'. $match[2], $xml_string);
}
}
$output = json_decode(json_encode(@simplexml_load_string($xml_string, 'SimpleXMLElement', ($options['flatten_cdata'] ? LIBXML_NOCDATA : 0))), ($options['use_objects'] ? false : true));
// Cast string values "true" and "false" to booleans
if ($options['convert_booleans']) {
$bool = function(&$item, $key) {
if (in_array($item, array('true', 'TRUE', 'True'), true)) {
$item = true;
} elseif (in_array($item, array('false', 'FALSE', 'False'), true)) {
$item = false;
}
};
array_walk_recursive($output, $bool);
}
return $output;
}
Si vous souhaitez convertir uniquement une partie spécifique du XML en JSON, vous pouvez utiliser XPath pour récupérer ceci et le convertir en JSON.
<?php
$file = @file_get_contents($xml_File, FILE_TEXT);
$xml = new SimpleXMLElement($file);
$xml_Excerpt = @$xml->xpath('/states/state[@id="AL"]')[0]; // [0] gets the node
echo json_encode($xml_Excerpt);
?>
Veuillez noter que si Xpath est incorrect, cela mourra avec une erreur. Donc, si vous déboguez cela via des appels AJAX, je vous recommande également de consigner les corps de réponse.
On dirait que la variable $state->name
contient un tableau. Vous pouvez utiliser
var_dump($state)
À l'intérieur du foreach
pour tester cela.
Si c'est le cas, vous pouvez changer la ligne à l'intérieur du foreach
en
$states[]= array('state' => array_shift($state->name));
Pour le corriger.
La question ne le dit pas, mais généralement PHP renvoie JSON à une page web.
Je trouve beaucoup plus facile de convertir le XML en JSON dans le navigateur / page via une lib JS, par exemple:
https://code.google.com/p/x2js/downloads/detail?name=x2js-v1.1.3.zip
Toutes les solutions ici ont des problèmes!
... Lorsque la représentation nécessite une interprétation XML parfaite (sans problèmes avec les attributs) et de reproduire tous les text-tag-text-tag-text -... et l'ordre des balises. Aussi bon rappelez-vous ici que JSON object " est un ensemble non ordonné "(pas répéter les clés et les clés ne peuvent pas avoir d'ordre prédéfini)... Mêmexml2json de ZF est faux (!) parce que pas préserver exactement la structure XML.
Toutes les solutions ici ont des problèmes avec ceci XML simple,
<states x-x='1'>
<state y="123">Alabama</state>
My name is <b>John</b> Doe
<state>Alaska</state>
</states>
... La solution @ FTav semble meilleure que la solution à 3 lignes, mais a aussi peu de bug lorsqu'elle est testée avec ce XML.
L'ancienne solution est la meilleure (pour une représentation sans perte)
La solution, aujourd'hui bien connue sous le nom de jsonML, est utilisée par Zorba project et d'autres, et a été présentée pour la première fois en ~2006 ou ~2007, par (séparément) Stephen McKamey et John Snelson.
// the core algorithm is the XSLT of the "jsonML conventions"
// see https://github.com/mckamey/jsonml
$xslt = 'https://raw.githubusercontent.com/mckamey/jsonml/master/jsonml.xslt';
$dom = new DOMDocument;
$dom->loadXML('
<states x-x=\'1\'>
<state y="123">Alabama</state>
My name is <b>John</b> Doe
<state>Alaska</state>
</states>
');
if (!$dom) die("\nERROR!");
$xslDoc = new DOMDocument();
$xslDoc->load($xslt);
$proc = new XSLTProcessor();
$proc->importStylesheet($xslDoc);
echo $proc->transformToXML($dom);
Produire
["states",{"x-x":"1"},
"\n\t ",
["state",{"y":"123"},"Alabama"],
"\n\t\tMy name is ",
["b","John"],
" Doe\n\t ",
["state","Alaska"],
"\n\t"
]
Voir http://jsonML.org ou github.com/mckamey/jsonml . les règles de production de ce JSON sont basées sur l'élément JSON-analog,
Cette syntaxe est unélément définition et récurrence, avec element-list ::= element ',' element-list | element
.
$xml = simplexml_load_string($xml_string);
$json = json_encode($xml);
$array = json_decode($json,TRUE);
Ajoutez simplement ces trois lignes, vous obtiendrez la sortie correcte: -)
Après avoir cherché un peu toutes les réponses, j'ai trouvé une solution qui fonctionnait très bien avec mes fonctions JavaScript dans les navigateurs (y compris les consoles / outils de développement):
<?php
// PHP Version 7.2.1 (Windows 10 x86)
function json2xml( $domNode ) {
foreach( $domNode -> childNodes as $node) {
if ( $node -> hasChildNodes() ) { json2xml( $node ); }
else {
if ( $domNode -> hasAttributes() && strlen( $domNode -> nodeValue ) ) {
$domNode -> setAttribute( "nodeValue", $node -> textContent );
$node -> nodeValue = "";
}
}
}
}
function jsonOut( $file ) {
$dom = new DOMDocument();
$dom -> loadXML( file_get_contents( $file ) );
json2xml( $dom );
header( 'Content-Type: application/json' );
return str_replace( "@", "", json_encode( simplexml_load_string( $dom -> saveXML() ), JSON_PRETTY_PRINT ) );
}
$output = jsonOut( 'https://boxelizer.com/assets/a1e10642e9294f39/b6f30987f0b66103.xml' );
echo( $output );
/*
Or simply
echo( jsonOut( 'https://boxelizer.com/assets/a1e10642e9294f39/b6f30987f0b66103.xml' ) );
*/
?>
Il crée fondamentalement un nouveau DOMDocument, charge et fichier XML et traverse chacun des nœuds et des enfants obtenant les données / paramètres et les exportant dans JSON sans les signes " @ " ennuyeux.
Lien vers le fichier XML .
$templateData = $_POST['data'];
// initializing or creating array
$template_info = $templateData;
// creating object of SimpleXMLElement
$xml_template_info = new SimpleXMLElement("<?xml version=\"1.0\"?><template></template>");
// function call to convert array to xml
array_to_xml($template_info,$xml_template_info);
//saving generated xml file
$xml_template_info->asXML(dirname(__FILE__)."/manifest.xml") ;
// function defination to convert array to xml
function array_to_xml($template_info, &$xml_template_info) {
foreach($template_info as $key => $value) {
if(is_array($value)) {
if(!is_numeric($key)){
$subnode = $xml_template_info->addChild($key);
if(is_array($value)){
$cont = 0;
foreach(array_keys($value) as $k){
if(is_numeric($k)) $cont++;
}
}
if($cont>0){
for($i=0; $i < $cont; $i++){
$subnode = $xml_body_info->addChild($key);
array_to_xml($value[$i], $subnode);
}
}else{
$subnode = $xml_body_info->addChild($key);
array_to_xml($value, $subnode);
}
}
else{
array_to_xml($value, $xml_template_info);
}
}
else {
$xml_template_info->addChild($key,$value);
}
}
}