For a utility that asks a user to enter xml (say, from a file on a local disk), one can use jQuery's parseXML to convert the string into an XML document. This will work fine with well formed XML, but will fail silently if the XML is malformed.
For example:
xmltest=' <dog><arf>33 </arf></dog>';
xmlDoc = $.parseXML( xmltest) ;
xml = $(xmlDoc );
achild=xml.children(0).children(0);
alert('Content of child 1= '+achild.html();
will correctly yield: Content of child 1= 33
However
xmltest=' <dog><arf>33 </arf1></dog>';
xmlDoc = $.parseXML( xmltest) ;
xml = $(xmlDoc );
...
will fail silently.
More precisely; messages are displayed in the console. For example, Firefox displays:
[21:08:47.731] mismatched tag. Expected: </arf>
[21:08:47.731] Error: Invalid XML:<dog><arf>33 </arf1></dog>
Ie and chrome report similar messages.
The question: is there any way that javascript, or jquery, can detect this failure and access these error messages? The goal being to tell the user what kind of problems his XML file may have.
Note that one reason I am playing with this is to workaround the inability of Chrome to read local xml files using $.get (yes yes yes I understand it is a security thing, but it is also a pain in the neck if you want to use XML files to store a lot of attribute information, and don't want to run chrome from a command line. And what about IE?).
Using try and catch helps a bit...:
xmltest=' <dog><arf>33 </arf1></dog>';
try {
xmlDoc = $.parseXML( xmltest) ;
}
catch(e) {
wow='';
for (m in e) wow=wow+' : '+m+'='+e[m];
alert(e+':error== '+wow); return 1;
}
Yields in Firefox:
Error: Invalid XML: <dog><arf>33 </arf1></dog> :error== : fileName=file:///D:/docs/carCosts/jquery-1.10.2.js : lineNumber=516 : columnNumber=2
Related
I want to send by XMLHttpRequest a JSON object to a Perl Script (*.cgi)
But I can't decode the JSON object in the cgi file.
I always reveive the error message:
malformed JSON string, neither array, object, number, string or atom, at character offset 0 (before
"(end of string)")
This is my javascript code:
//ajax communication for receiver/transceiver
function doAjaxRequest(query)
{
if(whatReq == "")
{
alert('ERROR: Request-Type undefined');
return;
}
if (window.XMLHttpRequest)
{
arequest = new top.XMLHttpRequest(); // Mozilla, Safari, Opera
}
else if (window.ActiveXObject)
{
try
{
arequest = new ActiveXObject('Msxml2.XMLHTTP'); // IE 5
}
catch (e)
{
try
{
arequest = new ActiveXObject('Microsoft.XMLHTTP'); // IE 6
}
catch (e)
{
alert('ERROR: Request not possible');
return;
}
}
}
if (!arequest)
{
alert("Kann keine XMLHTTP-Instanz erzeugen");
return false;
}
else
{
var url = "****.cgi";
var dp = document.location.pathname;
arequest.open('post', url, true);
arequest.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
//receiver function
arequest.onreadystatechange = function()
{
switch (arequest.readyState)
{
case 4:
if (arequest.status != 200)
{
alert("Der Request wurde abgeschlossen, ist aber nicht OK\nFehler:"+arequest.status);
}
else
{
var content = arequest.responseText;
analyseResponse(content);
}
break;
default:
//alert("DEFAULT:" + arequest.readyState );
break;
}
}
//transceiver function
query="jsonObj=" + JSON.stringify({name:"John Rambo", time:"2pm"});
alert(query);
arequest.send(query)
}
}
And here the cgi file:
#!/usr/bin/perl
use CGI qw/:standard/;
use CGI::Carp qw(fatalsToBrowser);
use strict;
use warnings;
use JSON;
use Data::Dumper;
my $jsonObj = param('jsonObj');
my $json = JSON->new->utf8;
my $input = $json->decode( $jsonObj );
print Dumper(\$input);
Can you help me? I don't know how to access the JSON object.
Thank you very much.
This message says you've got non-JSON string in $jsonObj. One particulary common case is empty string. Try printing out raw content of $jsonObj and make sure you set up everything correctly for CGI::param to work and also check with browser's built in tools that it actually sends data.
Also I highly suggest you throwing away 10 years old ActiveX shit. You're using JSON.stringify and all the browsers that support it also support native XMLHttpRequest.
I was about to vote to put your question on hold because of insufficient information to reproduce and diagnose the problem, but then I realized that your question does contain enough clues to figure out what's wrong — they're just really well hidden.
Clue #1: Your error message says (emphasis mine):
malformed JSON string, neither array, object, number, string or atom, at character offset 0 (before "(end of string)")
This implies that your $jsonObj variable has length 0, i.e. it is empty.
So, what's causing it to be empty? Well, the Perl code looks like perfectly standard CGI stuff, so the problem must be either in your JS code, or in something that your haven't showed us (such as your web server config). Since we can't debug what we can't see, let's focus on your JS code, where we find...
Clue #2: There's something wrong with this line:
arequest.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
Of course, you can set any content type you want for a POST request, but CGI.pm expects to receive the content in one of the standard formats for HTML form submissions, i.e. either application/x-www-form-urlencoded or multipart/form-data. When it receives something labeled as application/json instead, it doesn't know how to parse it, and so won't. Thus, the param() method in your Perl script will return nothing, since, as far as it's concerned, the client sent no parameters that it could understand.
There should have been a warning about this somewhere in your web server error logs, but you presumably didn't think to check those. (Hint: you really should!)
(You could've also used the warningsToBrowser option of CGI::Carp to get those warnings sent as HTML comments, but I guess you weren't aware of that option, either. Also, to make that work reliably, you'd really need to use CGI::Carp before the CGI module, so that it can catch any early errors.)
Anyway, the fix is simple: just replace application/json in your JS code with application/x-www-form-urlencoded, since that's what what you're actually trying to send to the server. You should also make sure that your JSON data actually is properly URL-encoded before embedding it in the request, by passing the output of JSON.stringify() through encodeURIComponent(), like this:
var data = {name:"John Rambo", time:"2pm"};
var query = "jsonObj=" + encodeURIComponent(JSON.stringify(data));
(I'll also second Oleg V. Volkov's suggestion to get rid of all the obsolete ActiveX stuff in your JS code. In fact, you could do even better by using a modern JS utility library like, say, jQuery, which provides convenient wrapper functions so that you don't even have to mess with XMLHttpRequest directly.)
After some changes in my project (the changes were not even related to the xml parsing part) the jquery.parseXML stopped working in firefox and chrome (I haven't tried other browsers).
I even tested very simple xml strings as
$.parseXML('<a>a</a>');
with the following result:
"Error: Invalid XML: <a>a</a>"
The internal message (catched by jquery) is the following:
"Failed to execute 'parseFromString' on 'DOMParser': The provided value 'text/dom' is not a valid enum value of type SupportedType."
This is triggered by the following jquery code lines:
tmp = new DOMParser();
xml = tmp.parseFromString( data, "text/dom" );
I really don't know how this is possible, since my changes had nothing todo with the xml parsing.
Its working if I parse it without jquery and type "application/xml", but why did the DOMParser accept the "text/dom" type before ?
Edit
Somehow my jquery version which I installed through bower (version 2.1.4) differs from the official version:
My version:
xml = tmp.parseFromString( data, "text/dom" );
From jquerySource:
xml = tmp.parseFromString( data, "text/xml" );
But it worked with the old version for months.
Solution:
So I just switched the jQuery file and its working, somehow the bower version is outdated. Nevertheless its a strange behaviour and I don't really know why it was working before, and why it stopped working from one day to another.
This code is taking in an xml file and parse it and output as a table. Here's a piece of code(Firefox oriented):
function viewXML(inputForm) {
var URL=inputForm.URL.value; //input XML URL
// create document and load URL
var xmlDoc = document.implementation.createDocument("","doc",null);
xmlDoc.load(URL);
xmlDoc.onload = generateHTML(xmlDoc);
hWin = window.open("","sth", "height=800,width=600");
hWin.document.write(html_text);
hWin.document.close();
}
viewXML is an onClick function of a button. Now a piece of code in generateHTML():
function generateHTML(xmlDoc) {
...
caption=xmlDoc.getElementsByTagName("title").item(0).firstChild.nodeValue; //<1>
html_text += "<caption align='left'><h1>" + caption + "</h1></caption>";
...
}
A piece of XML file:
<catalog>
<title>Airbus_Aircraft_Families</title>
<aircraft>
<Airbus>A380</Airbus>
<Aircraft>A380</Aircraft>
<seats>555</seats>
<Range>15000km</Range>
<Wingspan>78.8m</Wingspan>
<Height>24.1m</Height>
</aircraft>
</catalog>
Problem:
This code works fine when I was running it in debug mode step-by-step-ly in Firebug. BUT if there's no breakpoints set up, when the program hits line <1>, there will show an error saying that:
xmlDoc.getElementsByTagName("title").item(0) is null , which is apparently not true.
And interestingly, when I tried adding a line
alert(xmlDoc.getElementsByTagName("title").length)
before the <1> line, it will pop up a window shows 0, but after I click "OK", the CORRECT title(from XML) will show up in the new window! As that in step-by-step mode.
Things I tried:
1, changing xmlDoc.getElementsByTagName("title").item(0) to xmlDoc.getElementsByTagName("title")[0]
Didn't work, debugger told me "xmlDoc.getElementsByTagName("title")[0] is undefined"
2, Removed all the whitespaces within or between tags in XML file, didn't help.
The issue is probably due to your document being loaded asynchronously. You can check document.async property here. I think if you set this property to false, your code will work.
// create document and load URL
var xmlDoc = document.implementation.createDocument("","doc",null);
xmlDoc.async = false;
xmlDoc.onload = generateHTML(xmlDoc);
xmlDoc.load(URL);
Note that I moved the line attaching an event handler to load event before the call to load function.
I have a javascript which receives info from a servlet using jQuery:
$.get("authenticate", {badge:$('input#badge').val()}, function(data) {
console.log("xml: "+data);
displayInfoReturn(data);
});
When I process the result in Safari, everything works great:
function displayInfoReturn(data) {
if (/load/.test(data)) { // ...process string
}
}
But the 'if' always returns false in firefox (haven't tried it yet in IE or Chrome). I also tried using indexOf != -1 and search != -1. Nothing works!
One curious thing I noticed however is when I print data to console:
console.log ("received... "+data);
it comes back with "received... [object XMLDocument]". So apparently it's not treating my data as a string. I tried data.toString() but that doesn't work either. So how can I get firefox to play fair here?
What does your servlet return? An application/xml document or just text/plain? What have you set in response.setContentType()? You seem to be expecting XML and Firefox seems to be telling that it's really an XML document, but yet you're treating it as text/plain with that regex .test(). I'm not sure about Safari, but it look like that it has overridden a toString() on the XML document object so that it returns the whole XML string so that your regex by coincidence works fine.
Without knowing the exact XML document it's hard to tell how exactly to fix it. If it's for example
<data>
<action>load</action>
</data>
Then you can check the presence of load value in the <action> tag using jQuery's own XML parsing facilities as follows:
function displayInfoReturn(data) {
if ($(data).find('action').text() == 'load') {
// ...
}
}
See also:
Easy XML consumption with jQuery
I'm using slightly modified sample code provided by the YUI team. When my source responds with something other than JSON (or just has a JSON syntax error) my browser (Safari) aborts script processing, preventing me from notifying the user there was a problem.
I'm definitely no JS guru, so this code may be a lot uglier than it has to be. The code is, roughly:
YUI().use("dump", "node", "datasource-get", "datasource-jsonschema", function(Y) {
var myDataSource = new Y.DataSource.Get({
source:"/some/json/source/?"}),
myCallback = {
success: function(e){
myResponse = e.response;
doSomething(myDataSource);
},
failure: function(e){
Y.get("#errors").setContent("<li>Could not retrieve data: " + e.error.message + "</li>");
}
};
myDataSource.plug(Y.Plugin.DataSourceJSONSchema, {
schema: {
resultListLocator: "blah.list",
resultFields: ["user", "nickname"]
}
});
myDataSource.sendRequest("foo=bar", myCallback);
}
I've tried wrapping the "var myDataSource" block in a try/catch, and I've also tried wrapping the whole YUI().use() block.
Is it possible to catch syntax errors? Do I have to replace the all-in-one DataSource.Get call with separate IO and parse calls?
Since you are requesting a local script, you can use Y.io + Y.JSON.parse inside a try/catch or Y.DataSource.IO + Y.DataSchema.JSON (+ Y.JSON).
The benefit of DataSource.Get is that it avoids the Same Origin Policy. However, it is less secure and less flexible. If it is not necessary, you should avoid using it.
The contract of DataSource.Get is that the server supports JSONP. The way this works is that Get adds a script node to the page with a src=(the url you provided)&callback=someDataSourceFunction.
The browser will request the resource at that url and one of two things will happen:
the server will respond with a JavaScript string in the form of someDataSourceFunction({"all":"your data"}); or
the server will return some text that can't be parsed as JavaScript.
In either event, that string is treated as the contents of a script node--it is parsed and executed. If it cannot be parsed, the browser will throw an error. There's no stopping this. While JSONP is technically not under the spec constraints of true JSON (even invalid JSON should parse and execute), you should always use pure JSON, and always use a server side lib to generate the JSON output (look on http://json.org for a list of libs in every conceivable language). Don't hand-roll JSON. It only leads to hours of debugging.
The problem is probably that the error happens at some level in the browser (Javascript parsing) before YUI even gets the occasion to report a failure.
It is notoriously hard to catch this kind of error in Safari, which does not implement window.onerror. In order to catch more errors with my Javascript library, bezen.org, I added try/catch in places where asynchronous code is triggered:
dynamic script loading (equivalent to your JSON download)
setTimeout/setTimer: I wrapped and replaced these browser functions to insert a try/catch which logs errors
You may be interested in having a look at the source code of the corresponding modules, which may be useful to you as is or as hints for the resolution of your problem:
bezen.dom.js Look for safelistener in appendScript method
bezen.error.js Check safeSetTimeout/safeSetInterval and catchError
Maybe try this before you "doSomething":
try
{
var test = YAHOO.lang.JSON.parse(jsonString);
...
}
catch (e)
{
alert('invalid json');
}