Link to home
Start Free TrialLog in
Avatar of markremms
markremms

asked on

Using JQuery to parse XML

OK - so I've got $(identifier).load working and now I want to manipulate some XML instead using $.ajax

Console is giving me no clues what is wrong with this code:


function getGroups() 
{
	{
		$.ajax(
			{
                type: "GET",
                url: "/client-getSubGroupsOfType.php",
                data: "'ParentGroupID'=26,'TypeID'=25",
                dataType: "xml",
                success: function(xml) {
                	alert('Success!');
                    $(xml).find('Groups').each(function() 
                    {
                        $(this).find('item').each(function() {
                            $('#Groups').append($('<p>' + $(this).attr('Id') + ' - ' + $(this).attr('Name') + '</p>'));
                    });
                });
             },
             error: function() 
             	{
                	 alert('error Groups');
				}
			});
	}
}

$(document).ready(function(){
  getGroups();
  });
;

Open in new window

Avatar of markremms
markremms

ASKER

Example of the XML returned insode the SOAP envelope

<Groups xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="tns:Group[13]">
	<item xsi:type="tns:Group">
		<GroupId xsi:type="xsd:int">7165</GroupId>
		<ParentGroupId xsi:type="xsd:int">26</ParentGroupId>
		<Name xsi:type="xsd:string">Some Name</Name>
		<Type xsi:type="xsd:string">Some type</Type>
		<TypeID xsi:type="xsd:int">10</TypeID>
	</item>
	<item xsi:type="tns:Group">
		<GroupId xsi:type="xsd:int">7165</GroupId>
		<ParentGroupId xsi:type="xsd:int">29</ParentGroupId>
		<Name xsi:type="xsd:string">Some Other Name</Name>
		<Type xsi:type="xsd:string">Some Other type</Type>
		<TypeID xsi:type="xsd:int">10</TypeID>
	</item>
</Groups>

Open in new window

OK - datatype should be "text" not "xml" but that's only part of the story.
Avatar of leakim971
Test page : http://jsfiddle.net/r70dcaok/

code to use on your side (not "#xml") :
$(xml).find('Groups').each(function() {
    $(this).find('item').each(function() {
        $('#Groups').append($('<p>' + $("groupid", this).text() + ' - ' + $(this).find("name").text() + '</p>'));
    });
});

Open in new window

Please see: http://iconoun.com/demo/temp_markremms.php

I think the original SOAP XML document was not valid - it lacked the namespace information.  So I modified it, to remove the attributes.  Here is the server-side script.
<?php // demo/temp_markremms_server.php
error_reporting(E_ALL);

$xml = <<<EOD
<?xml version="1.0"?>
<Groups>
	<item>
		<GroupId>7165</GroupId>
		<ParentGroupId>26</ParentGroupId>
		<Name>Some Name</Name>
		<Type>Some type</Type>
		<TypeID>10</TypeID>
	</item>
	<item>
		<GroupId>7165</GroupId>
		<ParentGroupId>29</ParentGroupId>
		<Name>Some Other Name</Name>
		<Type>Some Other type</Type>
		<TypeID>10</TypeID>
	</item>
</Groups>
EOD;
echo $xml;

// IS THIS VALID XML?
// $obj = SimpleXML_Load_String($xml);
// var_dump($obj);

Open in new window

And on the client side, this seemed to work.
<!DOCTYPE html>
<html dir="ltr" lang="en-US">
<head>
<meta charset="utf-8" />

<!-- SEE http://www.experts-exchange.com/Programming/Languages/Scripting/JavaScript/Q_28509243.html -->

<meta name="robots" content="noindex, nofollow" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">

<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
function getGroups(){
    $.ajax({
        type: "GET",
        url:  "temp_markremms_server.php",
        data: "ParentGroupID=26,TypeID=25",
        dataType: "text",
        success: function(xml){
            xmlDoc = $.parseXML( xml ),
            myxml = $( xmlDoc );
            $(myxml).each(function(){
                $(this).find("Groups").each(function(){
                    $(this).find('item').each(function(){
                        $(this).find('GroupId').each(function(){
                            GroupID = $(this).text();
                        });
                        $(this).find('Name').each(function(){
                            Name = $(this).text();
                        });
                        $('#Groups').append($('<p>' + GroupID + ' - ' + Name + '</p>'));
                    });
                });
            });
         }
         ,
         error: function(){
             alert('error Groups');
         }
     });
}

$(document).ready(function(){
    getGroups();
});
</script>

<title>HTML5 Page With AJAX + XML</title>
</head>
<body>

<div id="Groups"></div>

</body>
</html>

Open in new window

Thanks both of you.

Ray - I get warnings that my XML is invalid (as you suggested).

It is produced by a NuSOAP server so it's in a full SOAP envelope.  Is that the problem?
Here's a SOAP response example.  It has headers which I guess might be a problem. I've cut out a  load of irrelevant stuff but I think the structure is still OK

HTTP/1.1 200 OK Date: Mon, 01 Sep 2014 10:16:16 GMT Server: Apache/2.2.26 (Unix) mod_ssl/2.2.26 OpenSSL/1.0.1e-fips mod_jk/1.2.37 mod_bwlimited/1.4 X-Powered-By: PHP/5.4.26 X-SOAP-Server: NuSOAP/0.9.5 (1.123) Content-Length: 18946 Connection: close Content-Type: text/xml; charset=ISO-8859-1 

<?xml version="1.0" encoding="ISO-8859-1"?><SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="http://www.dir.org.uk/server.php?wsdl"><SOAP-ENV:Body><ns1:getSubGroupsOfTypeResponse xmlns:ns1="http://www.dir.org.uk/server.php?wsdl">

<Groups xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="tns:Group[13]">
  <item xsi:type="tns:Group">
    <GroupId xsi:type="xsd:int">7165</GroupId>
    <ParentGroupId xsi:type="xsd:int">26</ParentGroupId>
    <Name xsi:type="xsd:string">A Name</Name>
    <Type xsi:type="xsd:string">A type</Type>
    <TypeID xsi:type="xsd:int">10</TypeID>
  </item></Groups>
</ns1:getSubGroupsOfTypeResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Open in new window

Now you're undoubtedly starting to understand why some people believe that SOAP is the devil.  I don't go quite that far, but I've always found that RESTful API interfaces are much easier to use.

There is still something wrong with the XML according to Chrome:

Uncaught Error: Invalid XML: <?xml version="1.0" encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:xsd="http://www.w...<omitted>...}
Sorry got to dash - will be back in a couple of hours.
Yes I'm seeing that error too - is it because of the headers or are they ignored?
I don't know.  I gave up on SOAP (and especially NuSOAP) because of so many experiences just like this.  Opaque error messages, or useless messages like "Invalid XML" are incredible time-wasters.  And there is one other thing you need to know about PHP SOAP support.  It's not really very good at all.  For but one example, the same tag name in two different namespaces causes a tag name collision.  Leads one to wonder "what are namespaces for?"  This bug has been in the language for several years, suggesting that PHP is never going to fix it.

Do you have any other way to retrieve the data from the API?
No I don't - and having spent a fair amount of time (funded by a client) on learning how to deal with SOAP / nuSOAP and building the service I'm pretty committed to it!

I shall have to persevere at least for now I think unless I can provide an equivalent service which is more usable without charging for it.  Using SOAP was my suggestion based upon a bit of reading around -  seemed like the logical thing to use...

If this isn't fixable it's a hard (but useful) lesson to learn!
Maybe you can use the XML without using the SOAP tags?
I was wondering about that -just need to work out how to get rid of them if that's the way forward.  Going to check validity of the XML first though.
Check this one for an example of the XML without the SOAP tags.
https://www.experts-exchange.com/questions/28509243/Using-JQuery-to-parse-XML.html?anchorAnswerId=40296857#a40296857

If you have a server-side scripting language that is under your control it may be easy to generate the XML without the SOAP tags.
That link seems to be to this question....  infinite loop alert!  :-)
I can get nusoap to output json instead - maybe that will improve matters.
Yes, if you can get a JSON string it might be a much faster path to success!

But that aside, can you help me out about the "infinite loop alert?"  What browser gave that message?
Check this one for an example of the XML without the SOAP tags.
https://www.experts-exchange.com/questions/28509243/Using-JQuery-to-parse-XML.html?anchorAnswerId=40296857#a40296857

Sorry my British sense of humour might not have been direct enough!  The link here is to this page, so if you follow it you arrive back at this page which then has this link..to itself and so on.

Not a browser message - just me trying (and failing!) to be funny.
Oh, I understand now ;-)  You might be surprised how many in the E-E community don't actually read the posted comments!  And since E-E is making incremental changes to the web platform, I thought there might have actually been a redirect loop or something.  (I'm on the product advisory committee, so I try to look out for things like that).

Irony: the first casualty of the internet.
Outputting json instead of XML is straightforward - done it.

Can see the output in chrome but when I call it from javascript I get an empty object.   The browser console also reports the returned page as empty so I think the problem is in the script not the server side stuff.  Any ideas - it's probably something daft?:

function getGroups(){
$.ajax({
        type: "GET",
        url:  "client-getSubGroupsOfType.php",
        data: "ParentGroupID=26,TypeID=25",
        dataType: 'json',
        success: function(data) {
            Groups = data;
            var out = "";
		    var i;
		    for(i = 0; i < Groups.length; i++) {
		        out += '<p>' + Groups[i].GroupID + '&nbsp;' + Groups[i].Name + '</p>';
		    }
            $('#Groups').append($(out));
        }
});
}
$(document).ready(function(){
    getGroups();
});

Open in new window

Example json from browsing to the called page
[{"GroupId":7165,"ParentGroupId":26,"Name":"name1","Type":"Type1","TypeID":10,"AdminAddress1":"","AdminAddress2":"","AdminAddress3":"","AdminAddress4":"","AdminPostalTown":"","AdminCounty":"","AdminCountry":"UK","AdminPostcode":"","PhyAddress1":"","PhyAddress2":"","PhyAddress3":"","PhyAddress4":"","PhyPostalTown":"","PhyCounty":"","PhyPostcode":"","Website":"","Email":"","ClericalContacts":[],"CuriaContacts":[],"SGL":-1,"SubGroups":[]},{"GroupId":7169,"ParentGroupId":26,"Name":"name2","Type":"type1","TypeID":10,"AdminAddress1":"","AdminAddress2":"","AdminAddress3":"","AdminAddress4":"","AdminPostalTown":"","AdminCounty":"","AdminCountry":"UK","AdminPostcode":"","PhyAddress1":"","PhyAddress2":"","PhyAddress3":"","PhyAddress4":"","PhyPostalTown":"","PhyCounty":"","PhyPostcode":"","Website":"","Email":"","ClericalContacts":[],"CuriaContacts":[],"SGL":-1,"SubGroups":[]}]

Open in new window

Let me experiment a bit...
Try this: http://iconoun.com/demo/temp_markremms.php

<!DOCTYPE html>
<html dir="ltr" lang="en-US">
<head>
<meta charset="utf-8" />

<!-- SEE http://www.experts-exchange.com/Programming/Languages/Scripting/JavaScript/Q_28509243.html -->

<meta name="robots" content="noindex, nofollow" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">

<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
function getGroups(){
    $.ajax({
        type: "GET",
        url:  "temp_markremms_server.php",
        data: "ParentGroupID=26,TypeID=25",
        dataType: 'json',
        success: function(data) {
            Groups = data;
            var out = "";
            var i;
            for(i = 0; i < Groups.length; i++) {
                out += '<p>' + Groups[i].GroupId + '&nbsp;' + Groups[i].Name + '</p>';
            }
            $('#Groups').append($(out));
        }
    });
}
$(document).ready(function(){
    getGroups();
});

</script>

<title>HTML5 Page With AJAX + JSON</title>
</head>
<body>

<div id="Groups"></div>

</body>
</html>

Open in new window

Server side code:
<?php // demo/temp_markremms_server.php
error_reporting(E_ALL);

$jso = <<<EOD
[{"GroupId":7165,"ParentGroupId":26,"Name":"name1","Type":"Type1","TypeID":10,"AdminAddress1":"","AdminAddress2":"","AdminAddress3":"","AdminAddress4":"","AdminPostalTown":"","AdminCounty":"","AdminCountry":"UK","AdminPostcode":"","PhyAddress1":"","PhyAddress2":"","PhyAddress3":"","PhyAddress4":"","PhyPostalTown":"","PhyCounty":"","PhyPostcode":"","Website":"","Email":"","ClericalContacts":[],"CuriaContacts":[],"SGL":-1,"SubGroups":[]},{"GroupId":7169,"ParentGroupId":26,"Name":"name2","Type":"type1","TypeID":10,"AdminAddress1":"","AdminAddress2":"","AdminAddress3":"","AdminAddress4":"","AdminPostalTown":"","AdminCounty":"","AdminCountry":"UK","AdminPostcode":"","PhyAddress1":"","PhyAddress2":"","PhyAddress3":"","PhyAddress4":"","PhyPostalTown":"","PhyCounty":"","PhyPostcode":"","Website":"","Email":"","ClericalContacts":[],"CuriaContacts":[],"SGL":-1,"SubGroups":[]}]
EOD;

// IS THIS VALID JSON?
if ($obj = json_decode($jso))
{
    // WRITE THE RESPONSE
	echo $jso;
}
else trigger_error('[{"Error":"BogusJSON"}]', E_USER_ERROR);

Open in new window

I can't really test this from the original data source.  Here is the message I get:

XMLHttpRequest cannot load http://url-path-to/client-getSubGroupsOfType.php?ParentGroupID=26,TypeID=25.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://iconoun.com' is therefore not allowed access.
Am I correct in my understanding that you control the server-side PHP script?  If not, there is a large research project ahead.  If so, read on.

I believe (but cannot test) this.  The PHP server-side script that produces this data needs to send some header() information before writing the JSON string.

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Origin, Content-Type, Accept, Authorization, X-Request-With');
header('Access-Control-Allow-Credentials: true');

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Ray - you're spot on with that.  Many, many thanks.
Ray Paseur is exemplary.  He clearly cares about helping others to learn, takes time to understand and experiment and gives clear, reasoned explanations for his solutions.   He is emphatically not looking for the "quick points" but obviously gets real pleasure from helping complete strangers by offering the benefit of his experience.

Thanks Ray!
Thanks for the points and thanks for using E-E! ~Ray