How would I go around the cross-domain issue when parsing XML from a different server/domain? Could someone provide me with an example? The example doesn't have to be restricted to jQuery only, as JavaScript will also suffice.
To fully understand why pure cross-domain XML will not work, it helps to first look at how cross domain JSON is facilitated.
First, let's look at what happens when you make an AJAX request in jQuery:
$.ajax({
url: '/user.php?userId=123',
success: function(data) {
alert(data); // alerts the response
});
In the above example, the AJAX request is made relative to the domain. We know that if we attempt to add a different domain before the path, the request will fail with a security exception.
However, that's not to say that the browser cannot make requests to another domain. Here is an example that may be familiar to you:
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
Based on our knowledge of how to import JavaScript on the page, we see that it is possible to load a resource that exists on another domain!
JSONP is a concept that exploits this knowledge. JSONP stands for "JSON with padding", and it's success hinges on the fact that JavaScript Objects can be expressed using a string notation, and the fact that JavaScript script tags can load and run content from external domains.
Under the hood, jQuery's JSONP looks something like this although it may not be exact:
// programmatically load a script tag on the page using the given url
function loadRemoteData(url) {
var script = document.createElement("script");
script.setAttribute("type","text/javascript");
script.setAttribute("src", url);
document.getElementsByTagName("head")[0].appendChild(script);
}
Also, on the page somewhere, we define a callback handler:
function processData(jsonResult) {
alert(JSON.stringify(jsonResult)); //alert the JSON as a string
}
Here, we make the request:
// make a request for the data using the script tag remoting approach.
loadRemoteData("http://example.com/users.php?userId=123&callback=processData");
For this to work properly, our PHP script must both return the data in JSON format, and it must also add "padding" around the string in the form of a JavaScript function name that we may pass in as a parameter (i.e. "callback")
Thus, the response from the server may look something like this, if we were to look at it in the Firebug or Chrome NET tab:
processData( { "userId" : "123" , "name" : "James" , "email" : "example#example.com" } );
Because we know JavaScript content runs as soon as it's downloaded, our processData function we defined earlier is immediately called and is passed our JSON string as a parameter. It is then alerted, using JSON.stringify to convert the object back into a string.
Since it's an object, I could also access it's properties, like so:
function processData(jsonResult) {
alert(JSON.stringify(jsonResult)); //alert the JSON as a string
// alert the name and email
alert("User name is " + jsonResult.name + " and email is " + jsonResult.email);
}
Finally, let's move onto the main question: Can JSONP be used to fetch XML, or can we parse XML cross-domain? The answer, as others have pointed out, is a resounding NO, but let's look at why by using an example:
processData(<?xml version="1.0"><user><userid>12345</userid><name>James</name><email>example#example.com</email></user>);
Now, what will happen if raw XML is passed into the function? It will break, as JavaScript has no way to convert XML into JSON.
However, suppose we put the XML in quotes:
processData("<?xml version="1.0"><user><userid>12345</userid><name>James</name><email>example#example.com</email></user>");
Now, in this example, the jsonResult variable actually takes a string, which we can work with. Using some JavaScript XML parsing utilities, we could load that string into the XML DOM Parser and do stuff with it!
However, it's not pure XML, it's still a JavaScript response under the hood. The response type from the PHP server is still text/javascript, and we're still using a script tag to load what is really just plain JavaScript.
In summary, you could work with "XMLP" or XML with padding (I just made that up, it's not real!), but if you're going to go through all of the trouble of actually modifying your response to return a function callback wrapper, you may as well just convert your output to JSON and let the browser handle conversions automatically and natively and save yourself the trouble of having to use an XML parser.
But if for some reason it's easier to keep your data in XML format, you could modify the response and give it a JavaScript wrapper.
Cases where I could see this being useful might be if you have XML data from a legacy application stored in a database, and you return it to the client-side using script-tag remoting or JSONP calls.
I found a very good solution to retrieve xml from cross domain ajax request.
Since jQuery 1.5 you can use dataType "jsonp xml" (http://api.jquery.com/jQuery.ajax/) !
So i used this :
$.ajax({
type: "GET",
url: "http://yoururl",
dataType: "jsonp xml",
success: function(xmlResponse) { // process data }
});
Server side for my Webservices i used to encapsulate the xml string result within the callback created by jQuery:
private static Stream GetXmlPStream(string result, string callback)
{
if (result == null)
result = string.Empty;
result = EncodeJsString(result);
if (!String.IsNullOrEmpty(callback))
result = callback + "(" + result + ");";
byte[] resultBytes = Encoding.UTF8.GetBytes(result);
if (WebOperationContext.Current != null)
WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml";
return new MemoryStream(resultBytes);
}
and the magic method (i found it in another Stack thread) that you'll need to sanitize your xml string (so javascript can parse it) :
private static string EncodeJsString(string s)
{
StringBuilder sb = new StringBuilder();
sb.Append("\"");
foreach (char c in s)
{
switch (c)
{
case '\"':
sb.Append("\\\"");
break;
case '\\':
sb.Append("\\\\");
break;
case '\b':
sb.Append("\\b");
break;
case '\f':
sb.Append("\\f");
break;
case '\n':
sb.Append("\\n");
break;
case '\r':
sb.Append("\\r");
break;
case '\t':
sb.Append("\\t");
break;
default:
int i = (int)c;
if (i < 32 || i > 127)
{
sb.AppendFormat("\\u{0:X04}", i);
}
else
{
sb.Append(c);
}
break;
}
}
sb.Append("\"");
return sb.ToString();
}
Hope this will help !
I realize this is an old question, but I found this while searching. And also, I think the answer is for a slightly different question than the one posted here, so I want to add this answer which should work in at least jQuery 1.12 and later. I haven't tested in earlier versions.
OK, I want to request this URL: http://sample.domain/feeds/itemdata.xml
And I want to find the Item in that looks like this:
<Item>
<ProductItemNo>1228101530</ProductItemNo>
...
</Item>
This works, cross-domain:
$.ajax({
dataType: "xml",
url: "http://sample.domain/feeds/itemdata.xml",
success: function(xml) {
var itemdata = $(xml).find("ProductItemNo:contains('1228101530')").parent();
}
});
Related
I just started to work on calls to a php file which is present in a different server. I am aware of CORS which is essential for cross domain requests. I have been trying to call this file through ajax methods refering to other websites and tutorials and I have seen discussions to find a solution but they are not working for me. Please help.
here is my calling method:
$.ajax({
type: "GET",
url: "http://cs-server.usc.edu:27952/ResponseProg.php?callback=?", //Relative or absolute path to response.php file
datatype: "jsonp",
data: dataInput,
jsonp:'jsoncallback',
crossDomain:true,
success: function(data)
{
JSONObj = jQuery.parseJSON(data);
contentProvider("#rtrn");
if(JSONObj.ack != "No results found")
{
var paginate=setPager(0);
$("#pgn").html(paginate);
}
},
error: function() {
$("#rtrn").html("Data not retrieved successfully");
}
});
Here is my PHP code snippet:
<?php
#code for data processing...
$rsltjson = json_encode($result,JSON_UNESCAPED_SLASHES);
echo $_GET['jsoncallback']."(".$rsltjson.");";
?>
I am trying to accomplish this by using JSONP. Should I have any headers?
Are there any errors in my code?....How can I accomplish this? dataInput is the serialized form of form data
The CORS way
You need to put the appropriate header in your php script and output only the JSON:
<?php
header('Access-Control-Allow-Origin: *');
// rest of the code
// output JSON only
echo $rsltjson;
?>
Then using a XMLHttpRequest/ajax call should retrieve the data just fine as JSON without resorting to JSONP.
Mozilla has plenty to read about it
The JSONP way
Since the whole point of JSONP is to bypass cross-domain restrictions, calling a JSONP resource with XMLHttpRequest/ajax, a method in which cross-domain security is fully applied (presumably), is completely useless.
JSONP works by injecting code directly into your page, calling a function that you defined, which is why a JSONP url takes an argument. Therefore, the correct way to call your JSONP url is this:
<script>
function myDataFunc(data) {
JSONObj = jQuery.parseJSON(data);
contentProvider("#rtrn");
if(JSONObj.ack != "No results found") {
var paginate=setPager(0);
$("#pgn").html(paginate);
}
}
</script>
<script src="http://cs-server.usc.edu:27952/ResponseProg.php?jsoncallback=myDataFunc"></script>
The JSONP url will return something that looks like this:
myDataFunc({"a":"fhsfg","b":"qfdgers","c":"difgij"});
Since it is included as a script, it will be executed directly in your page, calling the function myDataFunc() that you defined earlier.
Also note that your php file use the GET parameter jsoncallback while your javascript calls the url with the parameter callback, which would not work.
Finally, you use jQuery.parseJSON(), which produces this error from your code:
SyntaxError: JSON.parse: unexpected character at line 1 column 2 of the JSON data
The reason can be found in the jQuery docs:
jQuery.parseJSON( json )
Description: Takes a well-formed JSON string and returns the resulting JavaScript value.
Passing in a malformed JSON string results in a JavaScript exception being thrown.
Your php script feeds your callback with a JSON object
{"a":"fhsfg","b":"qfdgers","c":"difgij"}
rather than a string representing a JSON object
'{"a":"fhsfg","b":"qfdgers","c":"difgij"}'
Note the surrounding quotes, which makes this data a string. We fix this in php by adding the quotes around the data:
echo $_GET['jsoncallback']."('".$rsltjson."');";
Obviously if your JSON data contains single quotes, you will have to escape them.
I have some PHP 5 code that is similar to this:
$result = myFunction(...); // return false, or doit action
$reply = array();
if ($result) {
$reply['doit'] = $result;
$reply['status'] = "a status html string";
} else {
$reply['content'] = "Some html text";
$reply['menu'] = "Some other html text";
$reply['status'] = "a different status html string";
}
return $reply;
The caller includes the fragment
$reply = somefunction();
echo json_encode($reply);
This reply is then sent to the client, where jquery passes it to my function
function handleReply(reply) {
if (reply.doit) {
handle action
}
if (reply.content) document.getElementById('content').innerHTML=reply.content;
if (reply.menu) document.getElementById('menu').innerHTML=reply.menu;
if (reply.status) document.getElementById('status').innerHTML=reply.status;
}
What I have been struggling with is that, when the doit branch of the if statement is executed, ($result is a string) the reply given to me by jquery is a string. When the content/menu/status side is taken ($result is false) then reply is an object.
I have added a second second index to the array and the result is the same. Although all strings are ASCII I have tried passing them through UTF8_encode. I have changed the name of the 'doit' index from 'action' in case that was triggering some behaviour in jquery.
Just to be clear, the reply, when it is wrong is (for example).
"{"doit":"obj=session&act=show&ID=3","status":"<p>Nic: Ian<br\/>Reseller: Coachmaster.co.uk<br\/>Status: SysAdmin <\/p>"}"
Which is a string. I expected:
{"doit":"obj=session&act=show&ID=3","status":"<p>Nic: Ian<br\/>Reseller: Coachmaster.co.uk<br\/>Status: SysAdmin <\/p>"}
Which is an object/array. This is also what my logging showed as being echoed.
I'm using php5.4.3 under windows 7 and Apache and php 5.3.10 under linux and nginx with the same results. jquery is version v1.7.2 in both. Also loaded is jQuery UI - v1.10.3 - 2013-07-02.
If it is a bug in jquery, its a very strange one. How can I prove it?
I think you rely on jQuery autodetection. Try:
header('Content-Type: application/json');
Once you get the string into JavaScript, you'd have to eval() it to turn it into a JSON object:
var reply_json = eval( reply );
Then you can access reply_json.content, reply_json.menu, and so on.
Obviously be careful about what it is you're eval'ing, make sure it's from a trusted source and so forth.
Maybe you can try:
jQuery.parseJSON()
Do you use $.getJSON() jquery method or $.ajax()?
When using $.ajax() method, if you don't specify dataType: "json" option when you are making ajax request, jQuery will use "intelligent guess" (by searching for response MIME type) to figure how to interpret server response (xml, html, plain, json object...). If it is unable to figure that automatically it will assume response is plain text and regular string will be returned in success handle.
You should use either $.getJSON() or specify dataType: "json":
$.ajax({
url: "....",
dataType: "json",
success: function(reply) { // success handle
// if not specifying dataType: "json",
// and if not using response headers to specify MIME type "application/json",
// reply will not be object but a string!
}
});
or, as Marek posted in his answer, specify MIME type in response headers.
PHP
echo '<data><![CDATA[ [{"x":0,"y":0,"src":"images/image.jpg"}] ]]></data>';
JS
$.post( 'getData.php', {}, _onResult );
_onResult = function( result)
{
console.log(result);
}
The above console.log outputs:
( on localhost, using WAMP ):
<data><![CDATA[ [{"x":0,"y":0,"src":"images/image.jpg/"}] ]]></data>
( on web hosting, using LINUX ):
<data><![CDATA[ [{"/x/":0,/"y/":0,/"src/":/"images/image.jpg/"}] ]]></data>
How can I get the same output in the second case?
or
Can I somehow convert the second output to be able to parse it with $.parseJSON ?
No problem with your script..
var xml = "<data>\
<![CDATA[ [{\"x\":0,\"y\":0,\"src\":\"images/image.jpg\"}] ]]>\
</data>";
var dataXML = $.parseXML(xml);
var json = $(dataXML).find('data').text();
console.log(json); // outputs [{"x":0,"y":0,"src":"images/image.jpg"}]
No error in this line too
var json = JSON.parse(json);
The problem could be about the browser, or the HTTP server reading the XML data and not automatically parsing it to be a JSON object due to the security reasons. Whatever the reason, this line is (by assuming automatic conversion) fails:
var json = $(dataXML).find('data').text();
Instead, change it with this:
var json = $.parseJSON($(dataXML).find('data').text()); // i think you can use $.parse() too.
This problem have occured to me a few times in various occasions on both platforms before, and I don't think a stable thing. Howeever, since there is no way that the browser (or JS interpreter) can "detect" that it's actually a JSON object (inside that XML), you must explicitly define it to be so, if you want to establish type safety of your stuff (which is a good programming method).
PS: "localhost" is very flexible about security, so I'd recommend not to rely on it very much :)
I have a form being AJAX'd in by jQuery on a page with multiple forms. I'm performing the following function, which is a wrapper for the $.ajax function:
function do_json_get(uri){
var ret = '';
var url = AJAX_URL + uri;
$.ajax({
type: 'GET',
url: url,
async: false,
success: function(data) {
ret = data.html;
},
dataType: 'json'
});
return ret;
}
When I go to the AJAX server directly (which is rendering the form in PHP), I get the raw JSON response - so I know the server is outputting to the browser, and the AJAX server is doing other things like setting the proper cookies, so I know that the connection is good (I get a 200 response code). Yet the data object is coming back null.
Here's a guess. If you're serving the page from the file system, Firefox (and I think Chrome) will see it as originating from a different domain. You'll get the 200 response, but no data.
Try Safari, or maybe give jsonp a shot in place of json.
EDIT:
Since you're getting the data from a different domain, it won't work. I think it is not allowed by the XMLHTTPRequest.
Oh boy. This question again.
AJAX: Asynchronous JavaScript And XML*. Asynchronous. Asynchronous. Asynchronous.
Your function, do_json_get() returns ret before the success handler function in the AJAX call executes. The order of operation is not proceeding, here, from top to bottom. In other words, you just can't do it that way.
Because of its asynchronous nature, AJAX operates on callbacks, not return values. You need to change your paradigm. The success handler function needs to do whatever is required with data.html itself, or pass it off to another function.
* Yes, I know the XML part of the original acronym is largely vestigial these days
I am using jquery-1.3.2 in an AJAX web application. I use the jQuery ajax $.post() method to submit requests to the server.
On the server I am using php to build an array and then json_encode the answer. Then on the client I use the callback function of the AJAX post method to process the response.
All works well until I use the $.post() method to send variables to the server. If I send variables to the server, the response I get back is [object Object] and therefore I am unable to parse it. I have a work around at the moment that when posting variables I request a HTML response and then I parse that.
So the code involved taken from my site is:
The Jax call:
$.post("inc/sendfeedback.php", {NAME: name,TYPE: type,EMAIL: email,COMMENT: comment}, function(data) {PostData(data);}, "json");
So the PostData code looks like this:
function ProcessData(data)
{
//alert(data);
var jo = eval("(" + data + ")");
if(jo.result == "true")
{
if(jo.data != "" && jo.element != "")
{
$(jo.element).html(jo.data);
}
}
SMessage(jo.error);
}
If I uncomment the above code the alert with have in it [object Object].
if I remove the Post variables from the call it works fine.
The server code look like this:
$arr = array ("result" => $result,"data" => $data,"error" => $error,"element" => $element);
echo(json_encode($arr));
Is this a bug with the jQuery library, I tried it with the 1.2 version however its was still present there? I also search the jQuery site and can not find anyone having this issue.
So I assume I am missing something. But what?
$.ajax({
url: "script.php",
global: false,
type: "POST",
data: {NAME: name,TYPE: type,EMAIL: email,COMMENT: comment},
dataType: "json",
contentType: "application/json",
success: function(data){
alert(data.result);
}
}
No need to eval, jQuery evals/parses it before calling the success callback.
eval = pure evil
http://docs.jquery.com/Ajax/jQuery.ajax#options
Because you are using an associative PHP array, json_encode will return a string representation of a Javascript Object and not a Javascript Array. However, you should still be able to process it in a similar fashion to an array:
for (var key in data)
{
var item = data[key];
}
I would strongly recommend you download Firefox+Firebug addon and use the console API for debugging/dumping what is being returned by the server.
I have since registered and now can't post comments into this thread without reputation and can not see any easy method to claim this question as mine.
Deviant, your suggestion of using the $.ajax() method worked. Reason it didnt work for me the first time was I submitted the post data as a JSON object when the server code was expecting POST data.
So I fixed my javascript to call the server script correctly and everything works exactly as it should.
So the conclusion is, the $.post() method has a bug in it. I have not tracked it down but line 3633 is were the post method makes the call. I started digging however have not yet found the issue.
I qualify this by the fact the $.ajax() to the same server script and the same javascript processes the response and it all works, use the $.post method and my script fails with the return even through the return object appears to be a valid JSON object.
Thanks for the help guys. Now to go and remove all my $.post calls for $.ajax calls.
The result of all this can be seen at www.pygames.net
Cheers
Shane
a.k.a FrogSkin