J'essaye de convertir xml en json en php. Si je fais une conversion simple en utilisant xml simple et json_encode, aucun des attributs du show xml.
$xml = simplexml_load_file("states.xml");
echo json_encode($xml);
Donc, j'essaye de l'analyser manuellement comme ceci.
foreach($xml->children() as $state)
{
$states[]= array('state' => $state->name);
}
echo json_encode($states);
et la sortie pour state est {"state":{"0":"Alabama"}}
plutôt que {"state":"Alabama"}
Qu'est-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"
}
}
}
Je l'ai compris. json_encode traite les objets différemment des chaînes. Je jette l'objet sur une chaîne et cela fonctionne maintenant.
foreach($xml->children() as $state)
{
$states[]= array('state' => (string)$state->name);
}
echo json_encode($states);
Json & Array à partir de XML en 3 lignes:
$xml = simplexml_load_string($xml_string);
$json = json_encode($xml);
$array = json_decode($json,TRUE);
Désolé de répondre à un ancien message, 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;
}
}
?>
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.
Dislaimer: .__ Je ne suis pas un natif de PHP, alors veuillez supporter de simples erreurs.
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"
}
]
}
]
}
]
}
Il faut garder à l’esprit les points suivants: Plusieurs balises portant le même nom peuvent être des frères et sœurs. D'autres solutions vont très probablement supprimer tout sauf le dernier frère. Pour éviter cela, chaque nœud, même s'il n'a qu'un seul enfant, est un tableau contenant un objet pour chaque instance de la variable. (Voir plusieurs "" éléments dans l'exemple)
Même l'élément racine, dont un seul doit exister dans un document XML valide, est stocké sous forme de tableau avec un objet de l'instance, uniquement pour avoir une structure de données cohérente.
Pour pouvoir distinguer le contenu du nœud XML et les attributs XML, les attributs de chaque objet sont stockés dans "$" et le contenu dans l'enfant "_".
Edit: J'ai oublié d'afficher le résultat de votre exemple de données d'entrée
{
"states" : [{
"state" : [{
"$" : {
"id" : "AL"
},
"name" : [{
"_" : "Alabama"
}
]
}, {
"$" : {
"id" : "AK"
},
"name" : [{
"_" : "Alaska"
}
]
}
]
}
]
}
Un écueil courant consiste à oublier que json_encode()
ne respecte pas les éléments avec un attribut textvalue et . Il en choisira une, c'est-à-dire dataloss . La fonction ci-dessous résout ce problème Si l’on décide d’utiliser la méthode json_encode
/decode
, la fonction suivante est conseillée.
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 ) );
de cette manière, <foo bar="3">Lorem</foo>
ne se retrouvera pas sous la forme {"foo":"Lorem"}
dans votre code JSON.
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
J'ai utilisé TypeConverter de Miles Johnson à cet effet. Il est installable avec 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);
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);
C'est une amélioration de la solution la plus votée d'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 <person my-attribute='name'>John</person>
correctement).
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 le récupérer 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 votre Xpath est incorrect, cela mourra avec une erreur. Donc, si vous déboguez cela par le biais d'appels AJAX, je vous recommande également de consigner le corps des réponses.
Cette solution gère les espaces de noms, les attributs et produit des résultats cohérents avec des éléments récurrents (toujours dans un tableau, même s'il n'y a qu'une occurrence) . Inspiré par sxiToArray () de ratfactor }.
/**
* <root><a>5</a><b>6</b><b>8</b></root> -> {"root":[{"a":["5"],"b":["6","8"]}]}
* <root a="5"><b>6</b><b>8</b></root> -> {"root":[{"a":"5","b":["6","8"]}]}
* <root xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"><a>123</a><wsp:b>456</wsp:b></root>
* -> {"root":[{"xmlns:wsp":"http://schemas.xmlsoap.org/ws/2004/09/policy","a":["123"],"wsp:b":["456"]}]}
*/
function domNodesToArray(array $tags, \DOMXPath $xpath)
{
$tagNameToArr = [];
foreach ($tags as $tag) {
$tagData = [];
$attrs = $tag->attributes ? iterator_to_array($tag->attributes) : [];
$subTags = $tag->childNodes ? iterator_to_array($tag->childNodes) : [];
foreach ($xpath->query('namespace::*', $tag) as $nsNode) {
// the only way to get xmlns:*, see https://stackoverflow.com/a/2470433/2750743
if ($tag->hasAttribute($nsNode->nodeName)) {
$attrs[] = $nsNode;
}
}
foreach ($attrs as $attr) {
$tagData[$attr->nodeName] = $attr->nodeValue;
}
if (count($subTags) === 1 && $subTags[0] instanceof \DOMText) {
$text = $subTags[0]->nodeValue;
} elseif (count($subTags) === 0) {
$text = '';
} else {
// ignore whitespace (and any other text if any) between nodes
$isNotDomText = function($node){return !($node instanceof \DOMText);};
$realNodes = array_filter($subTags, $isNotDomText);
$subTagNameToArr = domNodesToArray($realNodes, $xpath);
$tagData = array_merge($tagData, $subTagNameToArr);
$text = null;
}
if (!is_null($text)) {
if ($attrs) {
if ($text) {
$tagData['_'] = $text;
}
} else {
$tagData = $text;
}
}
$keyName = $tag->nodeName;
$tagNameToArr[$keyName][] = $tagData;
}
return $tagNameToArr;
}
function xmlToArr(string $xml)
{
$doc = new \DOMDocument();
$doc->loadXML($xml);
$xpath = new \DOMXPath($doc);
$tags = $doc->childNodes ? iterator_to_array($doc->childNodes) : [];
return domNodesToArray($tags, $xpath);
}
Exemple:
php > print(json_encode(xmlToArr('<root a="5"><b>6</b></root>')));
{"root":[{"a":"5","b":["6"]}]}
Si vous êtes un utilisateur d'ubuntu, installez un lecteur xml (j'ai php 5.6. Si vous en avez d'autres, trouvez le paquet et installez-le)
Sudo apt-get install php5.6-xml
service Apache2 restart
$fileContents = file_get_contents('myDirPath/filename.xml');
$fileContents = str_replace(array("\n", "\r", "\t"), '', $fileContents);
$fileContents = trim(str_replace('"', "'", $fileContents));
$oldXml = $fileContents;
$simpleXml = simplexml_load_string($fileContents);
$json = json_encode($simpleXml);
... Lorsque la représentation nécessite une interprétation XML parfaite (sans problème d'attributs) et de reproduire tous les tags text-tag-text-tag-text -... et les balises. Rappelez-vous également que objet JSON "est un ensemble non ordonné" (les clés ne sont pas répétées et les clés ne peuvent pas avoir un ordre prédéfini) ... Même xml2json de ZF est faux (!) car pas préserver exactement la structure XML.
Toutes les solutions ici ont des problèmes avec ce simple XML,
<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 qu'une solution à 3 lignes, mais présente également peu de bogues lors des tests avec ce XML.
La solution, aujourd'hui connue sous le nom de jsonML, est utilisée par projet Zorba 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 le JSON-analogique element,
Cette syntaxe est une définition et une récurrence de élément, avec element-list ::= element ',' element-list | element
.
Après avoir étudié un peu toutes les réponses, je suis arrivé à une solution qui fonctionnait parfaitement avec les fonctions JavaScript de tous 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' ) );
*/
?>
Fondamentalement, il crée un nouveau document DOMDocument, des charges et un fichier XML, et parcourt chacun des noeuds et des enfants, récupérant les données/paramètres et les exportant au format JSON sans les signes "@" gênants.
Lien vers le fichier XML .
La question ne le dit pas, mais d'habitude PHP renvoie JSON à une page Web.
Je trouve beaucoup plus facile de convertir le XML en JSON dans le navigateur/la page via une lib. JS, par exemple:
https://code.google.com/p/x2js/downloads/detail?name=x2js-v1.1.3.Zip
On dirait que la variable $state->name
contient un tableau. Vous pouvez utiliser
var_dump($state)
dans la foreach
pour tester cela.
Si tel est le cas, vous pouvez modifier la ligne à l'intérieur de la foreach
en
$states[]= array('state' => array_shift($state->name));
pour le corriger.
This is better solution
$fileContents= file_get_contents("https://www.feedforall.com/sample.xml");
$fileContents = str_replace(array("\n", "\r", "\t"), '', $fileContents);
$fileContents = trim(str_replace('"', "'", $fileContents));
$simpleXml = simplexml_load_string($fileContents);
$json = json_encode($simpleXml);
$array = json_decode($json,TRUE);
return $array;