193

I am trying to convert xml to json in php. If I do a simple convert using simple xml and json_encode none of the attributes in the xml show.

$xml = simplexml_load_file("states.xml");
echo json_encode($xml);

So I am trying to manually parse it like this.

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

and the output for state is {"state":{"0":"Alabama"}} rather than {"state":"Alabama"}

What am I doing wrong?

XML:

<?xml version="1.0" ?>
<states>
    <state id="AL">     
    <name>Alabama</name>
    </state>
    <state id="AK">
        <name>Alaska</name>
    </state>
</states>

Output:

[{"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"
}
}
}
Bryan Hadlock
  • 2,626
  • 2
  • 17
  • 14

23 Answers23

547

Json & Array from XML in 3 lines:

$xml = simplexml_load_string($xml_string);
$json = json_encode($xml);
$array = json_decode($json,TRUE);
Ali Khaki
  • 1,184
  • 1
  • 13
  • 24
Antonio Max
  • 8,627
  • 6
  • 43
  • 42
  • the best I get. BTW how about large xml around 150MB. How much memory it'll take? – Sabbir Jun 05 '14 at 06:16
  • split the big file to small – iXcoder Jul 02 '14 at 10:31
  • 67
    This solution is not flawless. It completely discards XML attributes. So `John` is interpreted as `John`. – Jake Wilson Jan 26 '15 at 19:57
  • Jackobud, well then you are talking about a specific structure. for the general purposes what Antonio provided is just great. – useless Apr 01 '15 at 00:51
  • 16
    $xml = simplexml_load_string($xml_string,'SimpleXMLElement',LIBXML_NOCDATA); to flatten cdata elements. – txyoji Jul 14 '15 at 17:29
  • @AntonioMax and others, try ` Alabama

    John

    Alaska
    `, it lost tag order, so it is a **bug**... The solution is to change representation-map, see http://stackoverflow.com/a/39889010/287948
    – Peter Krauss Oct 06 '16 at 14:15
  • I have made an improved version of this which also works with namespaces. See answer further below (http://stackoverflow.com/a/40866796/2404541) – TheStoryCoder Nov 29 '16 at 13:14
  • 38
    @JakeWilson maybe it's the 2 years that have passed, and various version fixes, but on PHP 5.6.30, this method produces ALL of the data. Attributes are stored in the array under the `@attributes` key, so it works absolutely flawlessly, and beautifully. 3 short lines of code solve my problem beautifully. – Alex Feb 07 '17 at 16:12
  • 2
    This doesn't work if you have multiple namespaces, you can choose only one, which will pass into the $json_string :'( – jirislav Nov 26 '17 at 05:49
  • @AlexanderMP Not flawless, sorry. https://3v4l.org/S3jP8 This solution requires attributes to be only on parent to work well. – nanocv Jan 18 '18 at 13:30
  • 1
    Keep in mind that with this solution, when there may be multiple nodes with same name, one node will result in a key just pointing to an element, but multiple nodes will result in key pointing to _array_ of elements: `123456123` -> `{"item":[{"a":["123","456"]},{"a":"123"}]}`. A solution at php.net [by ratfactor](http://php.net/manual/en/class.simplexmliterator.php#92323) solves that issue by always storing elements in an array. – Klesun Jan 07 '19 at 21:44
  • 1
    @AlexanderMP I'm running 7.1.15 and it still doesn't include the attributes in `module.phpjs/module.js`. It includes the one in but not in ! – TheStoryCoder Apr 03 '19 at 16:22
  • @txyoji This answer of stripping our CDATA was something I was looking for hours. Excellent answer. – Marc Pope Oct 03 '19 at 01:10
42

Sorry for answering an old post, but this article outlines an approach that is relatively short, concise and easy to maintain. I tested it myself and works pretty well.

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;
    }
}
?>
Coreus
  • 5,360
  • 3
  • 35
  • 50
  • 7
    This will not work if you have multiple instances of the same tag in your XML, json_encode will end up only serializing the last instance of the tag. – ethree Nov 07 '13 at 20:50
  • 2
    To everyone looking at this old answer: Please bear in mind the times this was written in, and perhaps consider more modern approaches. – Coreus May 26 '21 at 21:42
37

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);
Bryan Hadlock
  • 2,626
  • 2
  • 17
  • 14
22

I guess I'm a bit late to the party but I have written a small function to accomplish this task. It also takes care of attributes, text content and even if multiple nodes with the same node-name are siblings.

Dislaimer: I'm not a PHP native, so please bear with simple mistakes.

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);
    }
}   

Usage example:

$xml = simplexml_load_file("myfile.xml");
echo xml2js($xml);

Example Input (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>

Example output:

{"family":[{"$":{"name":"Johnson"},"child":[{"$":{"name":"John","age":"5"},"toy":[{"$":{"status":"old"},"_":"Trooper"},{"$":{"status":"old"},"_":"Ultrablock"},{"$":{"status":"new"},"_":"Bike"}]}]}]}

Pretty printed:

{
    "family" : [{
            "$" : {
                "name" : "Johnson"
            },
            "child" : [{
                    "$" : {
                        "name" : "John",
                        "age" : "5"
                    },
                    "toy" : [{
                            "$" : {
                                "status" : "old"
                            },
                            "_" : "Trooper"
                        }, {
                            "$" : {
                                "status" : "old"
                            },
                            "_" : "Ultrablock"
                        }, {
                            "$" : {
                                "status" : "new"
                            },
                            "_" : "Bike"
                        }
                    ]
                }
            ]
        }
    ]
}

Quirks to keep in mind: Several tags with the same tagname can be siblings. Other solutions will most likely drop all but the last sibling. To avoid this each and every single node, even if it only has one child, is an array which hold an object for each instance of the tagname. (See multiple "" elements in example)

Even the root element, of which only one should exist in a valid XML document is stored as array with an object of the instance, just to have a consistent data structure.

To be able to distinguish between XML node content and XML attributes each objects attributes are stored in the "$" and the content in the "_" child.

Edit: I forgot to show the output for your example input data

{
    "states" : [{
            "state" : [{
                    "$" : {
                        "id" : "AL"
                    },
                    "name" : [{
                            "_" : "Alabama"
                        }
                    ]
                }, {
                    "$" : {
                        "id" : "AK"
                    },
                    "name" : [{
                            "_" : "Alaska"
                        }
                    ]
                }
            ]
        }
    ]
}
FTav
  • 389
  • 4
  • 13
  • Can it parse large XML data? – Volatil3 Mar 01 '16 at 18:09
  • 2
    This solution **is better** because not discards XML attributes. See also why this complex structure is better than simplified ones, at http://www.xml.com/lpt/a/1658 (see "Semi-Structured XML").... Ops, for CDATA, as @txyoji suggested to flatten CDATA elements `$xml = simplexml_load_file("myfile.xml",'SimpleXMLElement',LIBXML_‌​NOCDATA);` . – Peter Krauss Oct 06 '16 at 04:27
  • Many thanks for a custom function! It makes tuning pretty easy. Btw, added an edited version of your function that parses XML in a JS way: every entry has its own object (entries aren't stored in a single array if they have equal tagnames), thus the order is preserved. – lucifer63 Nov 06 '19 at 11:16
  • 1
    Error `Fatal error: Uncaught Error: Call to a member function getName() on bool` .. i think a version php is fail :-( .. please help! – KingRider Nov 26 '19 at 11:23
14

A common pitfall is to forget that json_encode() does not respect elements with a textvalue and attribute(s). It will choose one of those, meaning dataloss. The function below solves that problem. If one decides to go for the json_encode/decode way, the following function is advised.

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 ) );

by doing so, <foo bar="3">Lorem</foo> will not end up as {"foo":"Lorem"} in your JSON.

Matt
  • 5,315
  • 1
  • 30
  • 57
8

if you XML is a soap file, you can use this:

$xmlStr = preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $xmlStr);
$xml = new SimpleXMLElement($xmlStr);
return json_encode($xml);
Márcio Rossato
  • 971
  • 1
  • 11
  • 20
7

Try to use this

$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;

Or

You can use this library : https://github.com/rentpost/xml2array

Ajay Kumar
  • 1,314
  • 12
  • 22
5

Best solution which works like a charm

$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); // Remove // if you want to store the result in $json variable

echo '<pre>'.json_encode($simpleXml,JSON_PRETTY_PRINT).'</pre>';

Source

Alpha
  • 321
  • 6
  • 16
4

This solution handles namespaces, attributes, and produces consistent result with repeating elements (always in array, even if there is only one occurrence). Inspired by ratfactor's sxiToArray().

/**
 * <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);
}

Example:

php > print(json_encode(xmlToArr('<root a="5"><b>6</b></root>')));
{"root":[{"a":"5","b":["6"]}]}
Klesun
  • 12,280
  • 5
  • 59
  • 52
3

I've used Miles Johnson's TypeConverter for this purpose. It's installable using Composer.

You could write something like this using it:

<?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);
Husky
  • 5,757
  • 2
  • 46
  • 41
3

Optimizing Antonio Max answer:

$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);
3
        $content = str_replace(array("\n", "\r", "\t"), '', $response);
        $content = trim(str_replace('"', "'", $content));
        $xml = simplexml_load_string($content);
        $json = json_encode($xml);
        return json_decode($json,TRUE);

This worked for me

Brandon Stewart
  • 600
  • 1
  • 8
  • 12
2

If you would like to only convert a specific part of the XML to JSON, you can use XPath to retrieve this and convert that to 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);
?>

Please note that if you Xpath is incorrect, this will die with an error. So if you're debugging this through AJAX calls I recommend you log the response bodies as well.

ChrisR
  • 966
  • 14
  • 20
1

This is an improvement of the most upvoted solution by Antonio Max, which also works with XML that has namespaces (by replacing the colon with an underscore). It also has some extra options (and does parse <person my-attribute='name'>John</person> correctly).

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;
}
TheStoryCoder
  • 3,403
  • 6
  • 34
  • 64
  • 2
    One does not use Regex to parse XML, unless it's a simple XML with trivial structure and very predictable data. I can't stress enough how bad this solution is. This BREAKS DATA. Not to mention that it's incredibly slow (you parse with regex, and then you re-parse again?) and doesn't handle self-closing tags. – Alex Feb 07 '17 at 16:19
  • I don't think you really looked at the function. It doesn't use regex to do the actual parsing, only as a simple fix to deal with namespaces - which has been working for all my xml cases - and that it is working is the most important, rather than being "politically correct". You're welcome to improve it if you want, though! – TheStoryCoder Feb 13 '17 at 21:39
  • 2
    The fact that it has worked for you doesn't mean it's right. It's code like this that generates bugs that are immensely hard to diagnose, and generates exploits. I mean even looking superficially at XML specs on sites like this https://www.w3schools.com/xml/xml_elements.asp show a lot of reasons why this solution wouldn't work. Like I said, it fails to detect self-closing tags like ``, fails to address elements that start with, or contain underscores, which is allowed in XML. Fails to detect CDATA. And as I've said, it's SLOW. It's an O(n^2) complexity because of inner parsing. – Alex May 09 '17 at 17:41
  • 1
    The thing is that dealing with namespaces wasn't even asked here, and there are PROPER ways to deal with namespaces. Namespaces exist as a helpful construction, NOT to be parsed like that and turned into an abomination that won't be processed by any reasonable parser. And all you needed to do for that is not to create the contender for the prize of "slowest algorithm of 2016", but to do a bit of searching, to come up with a myriad of actual solutions, like this one http://stackoverflow.com/questions/16412047/parse-xml-namespaces-with-php-simplexml And to call this an improvement? Wow. – Alex May 09 '17 at 17:48
1
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;
Rashiqul Rony
  • 79
  • 1
  • 7
1

Found FTav's answer the most useful as it is very customizable, but his xml2js function has some flaws. For instance, if children elements has equal tagnames they all will be stored in a single object, this means that the order of elements will not be preserved. In some cases we really want to preserve order, so we better store every element's data in a separate object:

function xml2js($xmlnode) {
    $jsnode = array();
    $nodename = $xmlnode->getName();
    $current_object = array();

    if (count($xmlnode->attributes()) > 0) {
        foreach($xmlnode->attributes() as $key => $value) {
            $current_object[$key] = (string)$value;
        }
    }

    $textcontent = trim((string)$xmlnode);
    if (strlen($textcontent) > 0) {
        $current_object["content"] = $textcontent;
    }

    if (count($xmlnode->children()) > 0) {
        $current_object['children'] = array();
        foreach ($xmlnode->children() as $childxmlnode) {
            $childname = $childxmlnode->getName();
            array_push($current_object['children'], xml2js($childxmlnode, true));
        }
    }

    $jsnode[ $nodename ] = $current_object;
    return $jsnode;
}

Here is how it works. Initial xml structure:

<some-tag some-attribute="value of some attribute">
  <another-tag>With text</another-tag>
  <surprise></surprise>
  <another-tag>The last one</another-tag>
</some-tag>

Result JSON:

{
    "some-tag": {
        "some-attribute": "value of some attribute",
        "children": [
            {
                "another-tag": {
                    "content": "With text"
                }
            },
            {
                "surprise": []
            },
            {
                "another-tag": {
                    "content": "The last one"
                }
            }
        ]
    }
}
lucifer63
  • 784
  • 9
  • 32
1
    //main fuction ===========================
    function xml2array($responce)
    {
        $doc = new DOMDocument();
        $doc->loadXML($responce);
        $root = $doc->documentElement;
        $output = domNodeToArray($root);
        $output['@root'] = $root->tagName;
        return  $output;
    
    }
    
    //convert function =====================
    function domNodeToArray($node)
    {
        $output = [];
        switch ($node->nodeType) {
            case XML_CDATA_SECTION_NODE:
            case XML_TEXT_NODE:
                $output = trim($node->textContent);
                break;
            case XML_ELEMENT_NODE:
                for ($i = 0, $m = $node->childNodes->length; $i < $m; $i++) {
                    $child = $node->childNodes->item($i);
                    $v = domNodeToArray($child);
                    if (isset($child->tagName)) {
                        $t = $child->tagName;
                        if (!isset($output[$t])) {
                            $output[$t] = [];
                        }
                        $output[$t][] = $v;
                    } elseif ($v || $v === '0') {
                        $output = (string) $v;
                    }
                }
                if ($node->attributes->length && !is_array($output)) { // Has attributes but isn't an array
                    $output = ['@content' => $output]; // Change output into an array.
                }
                if (is_array($output)) {
                    if ($node->attributes->length) {
                        $a = [];
                        foreach ($node->attributes as $attrName => $attrNode) {
                            $a[$attrName] = (string) $attrNode->value;
                        }
                        $output['@attributes'] = $a;
                    }
                    foreach ($output as $t => $v) {
                        if (is_array($v) && count($v) == 1 && $t != '@attributes') {
                            $output[$t] = $v[0];
                        }
                    }
                }
                break;
        }
        return $output;
    }
    
    
    
    
    
    
    
    
    
    //REQUEST BY SOAP CLINTE==========================================================
    
      $sopeclient = new SoapClient('http://b2b.travel.us/FlightBooking.asmx?wsdl');
           $param = array('InputSTR'=>'
                <AirSearchQuery>
                <Master>
                    <CompanyId>*****</CompanyId>
                    <AgentId>1</AgentId>
                    <BranchId>1</BranchId>
                    <CoustmerType>AGNT</CoustmerType>
                </Master>
                <JourneyType>O</JourneyType>
                <Currency>USD</Currency>
                <Segments>
                    <Segment id="1">
                    <Origin>'.$request->depart.'</Origin>
                    <Destination>'.$request->destination.'</Destination>
                    <Date>'.$request->departOn.'</Date>
                    <Time></Time>
                    </Segment>
                </Segments>
          </AirSearchQuery>
         );
    
                
              $responce  = $sopeclient->SearchFare($param);
            }
    
    //RESPONCE GET ======================================================
     
     +"SearchFareResult": "<Itineraries><Itinerary><UniqueID>SK12041915MS7601445MS8750805</UniqueID><TrackID>AAL_LOS_24-02-2022_100_697074770_637812140580760438</TrackID><BaseFare>301.00</BaseFare><Taxes>224.90</Taxes><TotalPrice>525.90</TotalPrice><GrandTotal /><Currency>USD</Currency><FareType>RP</FareType><Adult><NoAdult>1</NoAdult><AdTax>224.9</AdTax><AdtBFare>301.00</AdtBFare></Adult><IndexNumber>0</IndexNumber><Provider>2A</Provider><ValCarrier>MS</ValCarrier><LastTicketingDate /><OutBound>3</OutBound><InBound>0</InBound><Sectors><Sector nearby="" isConnect="" isStopover=""><AirV>SK</AirV><AirlineName>Scandinavian Airlines</AirlineName><AirlineLogoPath>http://www.travelcation.us/AirlineLogo/SKs.gif</AirlineLogoPath><Class>U</Class><CabinClass><Code>Y</Code><Des>ECONOMY</Des></CabinClass><NoSeats>9</NoSeats><FltNum>1204</FltNum><Departure><AirpCode>AAL</AirpCode><Terminal /><Date>24-02-2022</Date><Time>19:15</Time><AirpName>Aalborg</AirpName><CityCode>AAL</CityCode><CityName>Aalborg</CityName><CountryCode>DK</CountryCode><CountryName>Denmark</CountryName><Day>Thu</Day><GEO_Code /></Departure><Arrival><AirpCode>CPH</AirpCode><Terminal>3</Terminal><Date>24-02-2022</Date><Time>20:00</Time><AirpName>Kastrup</AirpName><CityCode>CPH</CityCode><CityName>Copenhagen</CityName><CountryCode>DK</CountryCode><CountryName>Denmark</CountryName><Day>Thu</Day><GEO_Code /></Arrival><EquipType>Canadair Regional Jet CR9</EquipType><ElapsedTime>00:45</ElapsedTime><ActualTime>42:00</ActualTime><TechStopOver>0</TechStopOver><Status>OK</Status><isReturn>false</isReturn><OptrCarrier OptrCarrierDes="Cityjet">WX</OptrCarrier><MrktCarrier MrktCarrierDes="Scandinavian Airlines">SK</MrktCarrier><BaggageInfo>2 pcs</BaggageInfo><TransitTime time="00:00" /></Sector><Sector nearby="" isConnect="" isStopover=""><AirV>MS</AirV><AirlineName>EgyptAir</AirlineName><AirlineLogoPath>http://www.travelcation.us/AirlineLogo/MSs.gif</AirlineLogoPath><Class>V</Class><CabinClass><Code>Y</Code><Des>ECONOMY</Des></CabinClass><NoSeats>9</NoSeats><FltNum>760</FltNum><Departure><AirpCode>CPH</AirpCode><Terminal>3</Terminal><Date>25-02-2022</Date><Time>14:45</Time><AirpName>Kastrup</AirpName><CityCode>CPH</CityCode><CityName>Copenhagen</CityName><CountryCode>DK</CountryCode><CountryName>Denmark</CountryName><Day>Fri</Day><GEO_Code /></Departure><Arrival><AirpCode>CAI</AirpCode><Terminal>3</Terminal><Date>25-02-2022</Date><Time>20:05</Time><AirpName>Cairo Intl.</AirpName><CityCode>CAI</CityCode><CityName>Cairo</CityName><CountryCode>EG</CountryCode><CountryName>Egypt</CountryName><Day>Fri</Day><GEO_Code /></Arrival><EquipType>Boeing 738</EquipType><ElapsedTime>05:20</ElapsedTime><ActualTime>42:00</ActualTime><TechStopOver>0</TechStopOver><Status>OK</Status><isReturn>false</isReturn><OptrCarrier OptrCarrierDes="EgyptAir">MS</OptrCarrier><MrktCarrier MrktCarrierDes="EgyptAir">MS</MrktCarrier><BaggageInfo>2 pcs</BaggageInfo><TransitTime time="18:45">Connection of 18 Hours 45 Mins in Kastrup, Copenhagen, Denmark</TransitTime></Sector><Sector nearby="" isConnect="" isStopover=""><AirV>MS</AirV><AirlineName>EgyptAir</AirlineName><AirlineLogoPath>http://www.travelcation.us/AirlineLogo/MSs.gif</AirlineLogoPath><Class>L</Class><CabinClass><Code>Y</Code><Des>ECONOMY</Des></CabinClass><NoSeats>5</NoSeats><FltNum>875</FltNum><Departure><AirpCode>CAI</AirpCode><Terminal>3</Terminal><Date>26-02-2022</Date><Time>08:05</Time><AirpName>Cairo Intl.</AirpName><CityCode>CAI</CityCode><CityName>Cairo</CityName><CountryCode>EG</CountryCode><CountryName>Egypt</CountryName><Day>Sat</Day><GEO_Code /></Departure><Arrival><AirpCode>LOS</AirpCode><Terminal>D</Terminal><Date>26-02-2022</Date><Time>13:15</Time><AirpName>Murtala Muhammed</AirpName><CityCode>LOS</CityCode><CityName>Lagos</CityName><CountryCode>NG</CountryCode><CountryName>Nigeria</CountryName><Day>Sat</Day><GEO_Code /></Arrival><EquipType>Boeing 738</EquipType><ElapsedTime>05:10</ElapsedTime><ActualTime>42:00</ActualTime><TechStopOver>0</TechStopOver><Status>OK</Status><isReturn>false</isReturn><OptrCarrier OptrCarrierDes="EgyptAir">MS</OptrCarrier><MrktCarrier MrktCarrierDes="EgyptAir">MS</MrktCarrier><BaggageInfo>2 pcs</BaggageInfo><TransitTime time="12:00">Connection of 12 Hours 0 Mins in Cairo Intl., Cairo, Egypt</TransitTime></Sector></Sectors><FareBasisCodes><FareBasiCode><FareBasis>VOFLOWMS</FareBasis><Airline>MS</Airline><PaxType>ADT</PaxType><Origin /><Destination /><FareRst /></FareBasiCode>
    
    
    
            
        //call method===========================================
            $xml2json = xml2array($responce->SearchFareResult);
          
            print_r($xml2json);
    die;

//view result ====================================================

array:3 [▼
  "Itinerary" => array:63 [▼
    0 => array:17 [▼
      "UniqueID" => "SK12041915MS7601445MS8750805"
      "TrackID" => "AAL_LOS_24-02-2022_100_946417400_637812150487718359"
      "BaseFare" => "301.00"
      "Taxes" => "224.90"
      "TotalPrice" => "525.90"
      "GrandTotal" => []
      "Currency" => "USD"
      "FareType" => "RP"
      "Adult" => array:3 [▼
        "NoAdult" => "1"
        "AdTax" => "224.9"
        "AdtBFare" => "301.00"
      ]
      "IndexNumber" => "0"
      "Provider" => "2A"
      "ValCarrier" => "MS"
      "LastTicketingDate" => []
      "OutBound" => "3"
      "InBound" => "0"
      "Sectors" => array:1 [▼
        "Sector" => array:3 [▶]
      ]
      "FareBasisCodes" => array:1 [▶]
MOAZZAM RASOOL
  • 159
  • 1
  • 3
0

All solutions here have problems!

... When the representation need perfect XML interpretation (without problems with attributes) and to reproduce all text-tag-text-tag-text-... and order of tags. Also good remember here that JSON object "is an unordered set" (not repeat keys and the keys can't have predefined order)... Even ZF's xml2json is wrong (!) because not preserve exactly the XML structure.

All solutions here have problems with this simple XML,

    <states x-x='1'>
        <state y="123">Alabama</state>
        My name is <b>John</b> Doe
        <state>Alaska</state>
    </states>

... @FTav solution seems better than 3-line solution, but also have little bug when tested with this XML.

Old solution is the best (for loss-less representation)

The solution, today well-known as jsonML, is used by Zorba project and others, and was first presented in ~2006 or ~2007, by (separately) Stephen McKamey and 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);

Produce

["states",{"x-x":"1"},
    "\n\t    ",
    ["state",{"y":"123"},"Alabama"],
    "\n\t\tMy name is ",
    ["b","John"],
    " Doe\n\t    ",
    ["state","Alaska"],
    "\n\t"
]

See http://jsonML.org or github.com/mckamey/jsonml. The production rules of this JSON are based on the element JSON-analog,

enter image description here

This syntax is a element definition and recurrence, with
element-list ::= element ',' element-list | element.

Peter Krauss
  • 13,174
  • 24
  • 167
  • 304
0

After researching a little bit all of the answers, I came up with a solution that worked just fine with my JavaScript functions across browsers (Including consoles / Dev Tools) :

<?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' ) );
 */

?>

It basically creates a new DOMDocument, loads and XML file into it and traverses through each one of the nodes and children getting the data / parameters and exporting it into JSON without the annoying "@" signs.

Link to the XML file.

Xedret
  • 1,823
  • 18
  • 25
0

With accepted (antonio's) answer, from such source:

<MyData>
  <Level1 myRel="parent" myName="AAA">
    <Level2 myRel="child1" myName="BBB">
    <Level2 myRel="child2" myName="CCC">
      ...

you will get array like:

  'Level1' =>
  [
      0 =>
      [
          '@attributes' => 
          [
              'myRel' => 'parent'
              'myName'  => 'AAA'
          ],
          'Level2' => 
          [
              0 => 
              [
                  '@attributes' => 
                  [
                      'myRel'  => 'child_1'
                      'myName'   => 'BBB'
                  ],

So, if you want to have Key-Paired array (instead of 0 numbers), with your chosen key, i.e. myName:

  'Level1' =>
  [
      'AAA' =>
      [
          '@attributes' => 
          [
              'myRel' => 'parent'
              'myName'  => 'AAA'
          ],
          'Level2' => 
          [
              'BBB' => 
              [
                  '@attributes' => 
                  [
                      'myRel'  => 'child_1'
                      'myName'   => 'BBB'
                  ],

then use xmlToArrayByKey($xmlContent, 'myName'). Code Here:

public function xmlToArrayByKey($content, $keyName)
{
    try
    {
        $xml = simplexml_load_string($content, "SimpleXMLElement", LIBXML_NOCDATA );
        $array= json_decode( json_encode($xml), TRUE);
        return $this->xmlSetChild($array, $keyName);
    } catch (Exception $ex) {
        return ['xmlerror'=>$ex];
    }
}

public function xmlSetChild($array, $keyName, $step=0)
{
    $new_array= [];
    foreach ($array as $key_1=>$value_1)
    {
        if (is_array($value_1) && isset($value_1[0]))
        {
            foreach ($value_1 as $idx=>$value_2)
            {
                $keyValue = $value_2['@attributes'][$keyName];
                $new_array[$key_1][$keyValue] = $this->xmlSetChild($value_2, $keyName, $step+1);
            }
        }
        else{
            $new_array[$key_1]=$value_1;
        }
    }
    return $new_array;
}
T.Todua
  • 53,146
  • 19
  • 236
  • 237
-1
$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);
        }
    }
}
  • It is a small and universal solution based on an array of data can be a JSON transformed json_decode ...lucky – Octavio Perez Gallegos Jun 30 '16 at 22:45
  • 2
    In what way does this answer the original question? Your answer seems more complicated than the original question, and also doesn't seem to even mention JSON anywhere. – Dan R Jun 30 '16 at 22:54
-1

If you are ubuntu user install xml reader (i have php 5.6. if you have other please find package and install)

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);
Atul Baldaniya
  • 761
  • 8
  • 14
-1

Looks like the $state->name variable is holding an array. You can use

var_dump($state)

inside the foreach to test that.

If that's the case, you can change the line inside the foreach to

$states[]= array('state' => array_shift($state->name)); 

to correct it.

Michael Fenwick
  • 2,374
  • 2
  • 19
  • 28