Parsing JSON array of objects: undefined property - javascript

So, similar to my my previous question here (I was unsure whether to make a new question or edit the old one), I managed to sucessfully parse the body JSON response using the code seen below.
However when attempting to do the same to the event_calendar_date, I get this: Failed with: TypeError: Cannot read property 'und' of undefined. I would have assumed that to get to the next object in the array the method would be the same for both, however that does not appear to be the case.
JSON response from the server:
[
{
"revision_uid":"1",
"body":{
"und":[
{
"value":"Blah Blah.",
"summary":"",
"format":null,
"safe_value":"Blah Blah.",
"safe_summary":""
}
]
},
"event_calendar_date":{
"und":[
{
"value":"2015-07-20 14:00:00",
"value2":"2015-07-20 20:00:00",
"timezone":"America/New_York",
"timezone_db":"America/New_York",
"date_type":"datetime"
}
]
}
}
]
My Code:
Parse.Cloud.httpRequest({
method: "GET",
url: 'http://www.scolago.com/001/articles/views/articles',
success: function(httpResponse) {
var data = JSON.parse(httpResponse.text);
var articles = new Array();
for (var i = 0; i < data.length; i++) {
var Articles = Parse.Object.extend("Articles"),
article = new Articles(),
content = data[i];
article.set("body", content.body.und[0].value);
article.set("vid", content.vid);
article.set("title", content.title);
article.set("events", content.event_calendar_date.und[0].value);
articles.push(article);
};
// function save(articles);
Parse.Object.saveAll(articles, {
success: function(objs) {
promise.resolve();
},
error: function(error) {
console.log(error);
promise.reject(error.message);
}
});
},
error: function(error) {
console.log(error);
promise.reject(error.message);
}
});

I am afraid you didn't give us all code, or you are using not the code you posted in this question, because it should run fine, as you can see in this demo:
for (var i = 0; i < data.length; i++) {
var content = data[i];
console.log(content.event_calendar_date.und[0].value);
};
EDIT and solution
As I expected, JSON you are getting doesn't have field event_calendar_date for 2nd, and 3rd object. I checked in your full code on GitHub.
First object has correct event_calendar_date field, but next records don't have. See how is JSON you are getting formatted - http://www.jsoneditoronline.org/?id=b46878adcffe92f53f689978873a3474.
You need to check first if content.event_calendar_date is present in record in your current loop iteration or add event_calendar_date to ALL records in JSON response.

Related

Unexpected token 'o', "[object Obj"... is not a valid JSON

I have written a function in PHP which takes product information and user's desired calories information from a database and puts all of the information in an array. Afterwards it's encoded in JSON. Then the PHP file is used in a Javascript .html file where it should take the information I just said about from the PHP file and outputs the linear program results. The problem is that the .html file with Javascript in it returns an error in the console and a white page (screenshot).
The PHP file output is shown in the screenshot. I took the output and pasted it in a JSON validator, which shows that it's valid, so I don't see the issue here.
Any suggestions?
PHP(part):
// Add the product data to the products array
$products[] = [
'name' => $productRow['product_name'],
'price' => $price,
'calories' => $energyRow['energy_value'],
'UserCalories' => $userCaloriesRow['calories'],
];
}
// Output the products array as a JSON string
header('Content-Type: application/json');
echo json_encode($products, JSON_UNESCAPED_UNICODE);
$mysql->close();
return $products;
}
fetchProductsFromDatabase();
?>
Javascript:
<script src="https://unpkg.com/javascript-lp-solver#0.4.24/prod/solver.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
// Initialize the products and calories arrays
var products = [];
var calories = 0;
// Make an AJAX request to the PHP script that fetches the products and user's desired calories from the database
$.ajax({
url: 'fetchProductsFromDatabase.php',
success: function(response) {
// The response is a JSON object, so we need to parse it to get the products array and user's desired calories
var data = JSON.parse(response);
products = data.products;
// Set up the linear programming problem
var lp = new LinearProgramming(0, LinearProgramming.MAXIMIZE);
// Set up the constraints
var caloriesConstraint = {};
for (var i = 0; i < products.length; i++) {
caloriesConstraint[i] = products[i]['calories'];
}
lp.addConstraint(caloriesConstraint, LinearProgramming.EQUAL, calories);
// Set up the objective function
var priceObjective = {};
for (var i = 0; i < products.length; i++) {
priceObjective[i] = products[i]['price'];
}
lp.setObjective(priceObjective);
// Solve the linear program
var result = lp.solve();
// Print the results
for (var i = 0; i < products.length; i++) {
console.log(products[i]['name'] + ': ' + result[i]);
}
console.log('Total cost: ' + lp.getObjectiveValue());
},
error: function(jqXHR, textStatus, errorThrown) {
// There was an error with the request
console.log(jqXHR.responseText); // Output the response from the server
console.log(textStatus); // Output the error type
console.log(errorThrown); // Output the exception object, if available
}
});
</script>
The parameter passed to the success callback in jQuery's ajax method has already been parsed. It is not the string of JSON returned by the PHP, it is the result of reading that string of JSON into an ordinary JS object.
In short, JSON.parse has already been run before it gets to your code.
So instead of this:
success: function(response) {
// The response is a JSON object, so we need to parse it to get the products array and user's desired calories
var data = JSON.parse(response);
products = data.products;
// ...
You just want this:
success: function(data) {
// The response data is an object, from which we can get the products array and user's desired calories
products = data.products;
// ...
The reason you get the error you do is that JSON.parse expects a string (a string of JSON data), but you're passing it an object (the one jQuery has passed you). JavaScript "helpfully" turns the object into the string '[Object object]', and passes it to JSON.parse.
you need to stringify the JSON before parsing, something like:
JSON.parse(JSON.stringify(response));
Best regards,

ignore case in for loop when property does not exist

I am making a GET request to an endpoint.
This returns an array containing many objects, some of which contain a url for a photo.
If the individual object contains a photo I want to display it, if not just ignore.
I expected the following code to work, and ignore the cases where the photo does not exist, but I am still getting the following error message.
Uncaught TypeError: Cannot read property '0' of undefined(…)
$.get(url, function (data) {
for(var i = 0; i < data.length; i++){
if(data[i].media[0].img){
console.log(data[i].media[0].img);
}
}
});
$.get(url, function (data) {
for(var i = 0; i < data; i++){
if(data[i].media && data[i].media[0]){
console.log(data[i].media[0].img);
}
}
});

Displaying JSON data in Javascript loop

I am having an issue displaying json data. I have searched and found many examples, but for some reason I get the "Cannot read property 'length' of undefined" error, because "var object = notes.data;" comes back as undefined.
Why is object undefined? From everything I can see I am doing it right.
My json that is returned.
{
"data":[
{
"NumberOfAnswers":25,
"Answer":"51-89 Percent of all items in section are <b>IN STOCK<\/b>",
"Percent":54.35
},
{
"NumberOfAnswers":21,
"Answer":"90-100 Percent of all items in section are <b>IN STOCK<\/b>",
"Percent":45.65
}
]
}
And here is the function to display it. (With some debugging code as well)
function format(notes) {
console.log(notes); //this displays the json
var object = notes.data;
console.log(object);
var i;
for (var i = 0; i < object.length; i++) {
var Result = object[i];
var Answer = Result.Answer;
console.log(Answer)
}
}
Here is the ajax function:
$.ajax({
type: 'post',
url: '/rmsicorp/clientsite/pacingModal/surveyajax2.php',
success: function(result) {
if (row.child.isShown()) {
row.child.hide();
tr.removeClass('shown');
detailsrows.splice(RowID, 1);
} else {
row.child(format(result)).show();
tr.addClass('shown');
if (RowID === -1) {
detailsrows.push(tr.attr('id'));
}
}
}
});
Most likely, the response is still in string format. You can add JSON.parse to convert it to an object and then be able too access the data property.
Try modifying your function like this to see if it fixes the problem.
function format(notes) {
console.log(notes); //this displays the json
// The following converts the response to a javascript object
// and catches the exception if the response is not valid JSON
try {
notes = JSON.parse(notes);
} catch(e) {
console.log('notes is not valid JSON: ' + e);
}
var object = notes.data;
console.log(object);
var i;
for (var i = 0; i < object.length; i++) {
var Result = object[i];
var Answer = Result.Answer;
console.log(Answer)
}
}

Parse.Cloud.job promise not working

What I am trying to do here are:
Remove all contents in a class first, because every day the events.json file will be updated. I have my first question here: is there a better way to remove all contents from a database class on Parse?
Then I will send a request to get the events.json and store "name" and "id" of the result into a 2D array.
Then I will send multiple requests to get json files of each "name" and "id" pairs.
Finally, I will store the event detail into database. (one event per row) But now my code will terminate before it downloaded the json files.
Code:
function newLst(results) {
var event = Parse.Object.extend("event");
for (var i = 0; i < results.length; i++){
Parse.Cloud.httpRequest({
url: 'https://api.example.com/events/'+ results[i].name +'/'+ results[i].id +'.json',
success: function(newLst) {
var newJson = JSON.parse(newLst.text);
var newEvent = new event();
newEvent.set("eventId",newJson.data.id);
newEvent.set("eventName",newJson.data.title);
newEvent.save(null, {
success: function(newEvent) {
alert('New object created with objectId: ' + newEvent.id);
},
error: function(newEvent, error) {
alert('Failed to create new object, with error code: ' + error.message);
}
});
},
error: function(newLst) {
}
});
}
};
Parse.Cloud.job("getevent", function(request, status) {
var event = Parse.Object.extend("event");
var query = new Parse.Query(event);
query.notEqualTo("objectId", "lol");
query.limit(1000);
query.find({
success: function(results) {
for (var i = 0; i < results.length; i++) {
var myObject = results[i];
myObject.destroy({
success: function(myObject) {
},
error: function(myObject, error) {
}
});
}
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
var params = { url: 'https://api.example.com/events.json'};
Parse.Cloud.httpRequest(params).then(function(httpResponse) {
var results = [];
var jsonobj = JSON.parse(httpResponse.text);
for (var i = 0; i < jsonobj.data.length; i++) {
var tmp2D = {"name":"id"}
tmp2D.name = [jsonobj.data[i].name];
tmp2D.id = [jsonobj.data[i].id];
results.push(tmp2D);
}
newLst(results);
}).then(function() {
status.success("run job");
}, function(error) {
status.error(error);
});
});
I think my original answer is correct as a standalone. Rather than make it unreadable with the additional code, here it is made very specific to your edit.
The key is to eliminate passed callback functions. Everything below uses promises. Another key idea is decompose the activities into logical chunks.
A couple of caveats: (1) There's a lot of code there, and the chances that either your code is mistaken or mine is are still high, but this should communicate the gist of a better design. (2) We're doing enough work in these functions that we might bump into a parse-imposed timeout. Start out by testing all this with small counts.
Start with your question about destroying all instances of class...
// return a promise to destroy all instances of the "event" class
function destroyEvents() {
// is your event class really named with lowercase? uppercase is conventional
var query = new Parse.Query("event");
query.notEqualTo("objectId", "lol"); // doing this because the OP code did it. not sure why
query.limit(1000);
return query.find().then(function(results) {
return Parse.Object.destroyAll(results);
});
}
Next, get remote events and format them as simple JSON. See the comment. I'm pretty sure your idea of a "2D array" was ill-advised, but I may be misunderstanding your data...
// return a promise to fetch remote events and format them as an array of objects
//
// note - this differs from the OP data. this will evaluate to:
// [ { "name":"someName0", id:"someId0" }, { "name":"someName1", id:"someId1" }, ...]
//
// original code was producing:
// [ { "name":["someName0"], id:["someId0"] }, { "name":["someName1"], id:["someId1"] }, ...]
//
function fetchRemoteEvents() {
var params = { url: 'https://api.example.com/events.json'};
return Parse.Cloud.httpRequest(params).then(function(httpResponse) {
var results = [];
var remoteEvents = JSON.parse(httpResponse.text).data;
for (var i = 0; i < remoteEvents.length; i++) {
var remoteEvent = { "name": remoteEvents[i].name, "id": remoteEvents[i].id };
results.push(remoteEvent);
}
return results;
});
}
Please double check all of my work above regarding the format (e.g. response.text, JSON.parse().data, etc).
Its too easy to get confused when you mix callbacks and promises, and even worse when you're generating promises in a loop. Here again, we break out a simple operation, to create a single parse.com object based on one of the single remote events we got in the function above...
// return a promise to create a new native event based on a remoteEvent
function nativeEventFromRemoteEvent(remoteEvent) {
var url = 'https://api.example.com/events/'+ remoteEvent.name +'/'+ remoteEvent.id +'.json';
return Parse.Cloud.httpRequest({ url:url }).then(function(response) {
var eventDetail = JSON.parse(response.text).data;
var Event = Parse.Object.extend("event");
var event = new Event();
event.set("eventId", eventDetail.id);
event.set("eventName", eventDetail.title);
return event.save();
});
}
Finally, we can bring it together in a job that is simple to read, certain to do things in the desired order, and certain to call success() when (and only when) it finishes successfully...
// the parse job removes all events, fetches remote data that describe events
// then builds events from those descriptions
Parse.Cloud.job("getevent", function(request, status) {
destroyEvents().then(function() {
return fetchRemoteEvents();
}).then(function(remoteEvents) {
var newEventPromises = [];
for (var i = 0; i < remoteEvents.length; i++) {
var remoteEvent = remoteEvents[i];
newEventPromises.push(nativeEventFromRemoteEvent(remoteEvent));
}
return Parse.Promise.when(newEventPromises);
}).then(function() {
status.success("run job");
}, function(error) {
status.error(error);
});
});
The posted code does just one http request so there's no need for an array of promises or the invocation of Promise.when(). The rest of what might be happening is obscured by mixing the callback parameters to httpRequest with the promises and the assignment inside the push.
Here's a clarified rewrite:
Parse.Cloud.job("getevent", function(request, status) {
var promises = [];
var params = { url: 'https://api.example.com'};
Parse.Cloud.httpRequest(params).then(function(httpResponse) {
var results = [];
var jsonobj = JSON.parse(httpResponse.text);
for (var i = 0; i < jsonobj.data.length; i++) {
// some code
}
}).then(function() {
status.success("run job");
}, function(error) {
status.error(error);
});
});
But there's a very strong caveat here: this works only if ("// some code") that appears in your original post doesn't itself try to do any asynch work, database or otherwise.
Lets say you do need to do asynch work in that loop. Move that work to a promise-returning function collect those in an array, and then use Promise.when(). e.g....
// return a promise to look up some object, change it and save it...
function findChangeSave(someJSON) {
var query = new Parse.Query("SomeClass");
query.equalTo("someAttribute", someJSON.lookupAttribute);
return query.first().then(function(object) {
object.set("someOtherAttribute", someJSON.otherAttribute);
return object.save();
});
}
Then, in your loop...
var jsonobj = JSON.parse(httpResponse.text);
var promises = [];
for (var i = 0; i < jsonobj.data.length; i++) {
// some code, which is really:
var someJSON = jsonobj.data[i];
promises.push(findChangeSave(someJSON));
}
return Parse.Promise.when(promises);

Changing values of Parse column using Parse Code Cloud Javascript function

So its my first time writing Javascript so please bear with me. I wrote this function in order to query a class in my Parse.com application, and then after querying I want to set one of the columns (of type Boolean) to true.
I set up a test class with only 7 values in order to test.
The problem: only 3 out of 7 are being changed. Do I have to wait after each save? I know that waiting/sleeping in Javascript is "wrong" but I can't seem to find a solution.
Thanks in advance!
Additionally, when using iOS/Parse, I would like to check if the boolean value is undefined in Objective-C, I already tried to compare it to nil/NULL, an exception was thrown
Parse.Cloud.define("setYears", function(request, response) {
var object = new Parse.Query("testClass");
object.find({
success: function(results)
{
for (var i = 0; i < results.length; i++) {
results[i].set("testBool",true);// = true;
results[i].save(null,
{
success:function ()
{
response.success("Updated bool!");
},
error:function (error)
{
response.error("Failed to save bool. Error=" + error.message);
}
});
};
response.success();
}
})
});
It turned out to be not that difficult to solve as stated above. Just had to use saveAll instead of saving each object by itself. Here is the correct solution if anybody needs it:
Parse.Cloud.define("setYears", function(request, response) {
var object = new Parse.Query("testClass");
object.find({
success: function(results)
{
for (var i = 0; i < results.length; i++) {
results[i].set("testBool",true);// = true;
}
Parse.Object.saveAll(results,{
success: function(list) {
// All the objects were saved.
response.success("ok " ); //saveAll is now finished and we can properly exit with confidence :-)
},
error: function(error) {
// An error occurred while saving one of the objects.
response.error("failure on saving list ");
},
});
response.success();
}
})
});

Categories