As part of my WLAN Thermometer project, I am planning a small file management for the files stored on the ESP. In this context, I need to transfer a list of file names from the client to the server. Because there is also the wonderful ArduinoJSON library for the ESP 8266, I would like to pass the data as a JSON object. The first excerpt from the scripts.js of my webpage shows how to create the filelist (contains all available files at ESP Filesystem) and compile and transfer the deletelist (whose elements should be deleted).
let fileID = 0
for (i = 2; i < FDatas.length; i += 2)
{
let fileInfo = {
name: FDatas[i],
size: FDatas[i+1],
fileID: fileID,
marked: false};
fileList.push(fileInfo);
};
}
function deleteFiles() {
let deleteFileList = [];
let fileID = 0;
for (let i = 0; i < fileList.length; i++) {
if (fileList[i].marked == true) {
let keyname = 'fileID_' + String(fileID);
fileID += 1;
let newEntry = {[keyname]:fileList[i].name}
deleteFileList.push(newEntry);
}
}
if (deleteFileList.length > 0) {
var xhttp = new XMLHttpRequest();
var formData = JSON.stringify(deleteFileList);
xhttp.open("POST", "/deleteFiles");
xhttp.send(formData);
}
}
On the server side, communication is organized as follows:
In the setup part of the arduino code:
webserver.on("/deleteFiles", HTTP_POST, deleteFiles);
In the handler:
void deleteFiles() {
String input = webserver.arg("plain");
Serial println(input);
DynamicJsonDocument doc(2048);
DeserializationError err = deserializeJson(doc, input);
if (err) {
Serial.println(F("deserializeJson() failed with code "));
Serial.println(err.f_str());
}
JsonObject obj = doc.as<JsonObject>();
// Loop through all the key-value pairs in obj
for (JsonPair p : obj) {
Serial.println(p.key().c_str());
if (p.value().is<const char*>()) {
auto s = p.value().as<const char*>();
Serial.println(s);
}
}
webserver.send(200);
}
The result of these efforts is sobering. Nevertheless, the Serial.println(input); - command outputs the following,
[{"fileID_0":"/settings.json"},{"fileID_1":"/tdata.js"},{"fileID_2":"/scripts.js"}]
the passage through the JSON object does not result in key value pairs.
Where is my mistake? Thank you very much for your good advice.
1. Udate:
After first comment (Thank You) I've changed the arduino-code to:
void deleteFiles() {
String input = webserver.arg("plain");
Serial.println(input);
DynamicJsonDocument doc(2048);
DeserializationError err = deserializeJson(doc, input);
if (err) {
Serial.println(F("deserializeJson() failed with code "));
Serial.println(err.f_str());
}
JsonArray arr = doc.to<JsonArray>();
for(JsonVariant v : arr) {
Serial.println(v.as<const char*>());
}
webserver.send(200);
}
Unfortunately, the result is the same. No result in the loop.
Your json object consists of an array(each element of an array is indexed by a number like 0, 1, 2...), and within the array, there are these 3 json objects. So to access the data of each array element, you do doc[0] and so on. You can then access each key value pair with doc[0]['key'] notation.
StaticJsonDocument<192> doc;
DeserializationError error = deserializeJson(doc, input);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
const char* element1 = doc[0]["fileID_0"]; // "/settings.json"
const char* element2 = doc[1]["fileID_1"]; // "/tdata.js"
const char* element3 = doc[2]["fileID_2"]; // "/scripts.js"
I need to verify whether a particular node in the response body of an API is null or a string. How is it done using ChaiJS in postman tests?
Sample API response body:
[
{
"exercise_num": "1",
"expire_date": "2019-03-11T16:31:17.935Z",
"created_at": "2019-03-15T11:44:35.698Z"
},
{
"exercise_num": "2",
"expire_date": null,
"created_at": "2019-03-15T11:44:38.363Z"
}
]
I would like to verify that the expire_date node in the above sample API response body will either only contain null or a string data type and it won't return any other data type such as int, etc.
I have tried the following:
var jsonData = JSON.parse(responseBody);
pm.test('All expire_date contains either string or null', () => {
for (i = 0; i < jsonData.length; i++) {
if(jsonData[i].expire_date === null){
tests["expire_date is null"] = true;
}
else{
pm.expect(jsonData[i].expire_date).to.be.a('string');
}
}
});
The test passes.
I'm expecting if something like this can be done:
pm.test('All expire_date contains string', () => {
for (i = 0; i < jsonData.length; i++) {
pm.expect(jsonData[i].expire_date).to.be.a('string' || null);
}
});
I believe there is no direct way with || operator though, you can check with data types. As null is an object type in javaScript, write the test case as follows:
var jsonData = JSON.parse(responseBody);
pm.test('All expire_date contains string', () => {
for (i = 0; i < jsonData.length; i++) {
pm.expect(typeof jsonData[i].expire_date).to.be.oneOf(['string', 'object']);
}
});
That's probably an easy way, and can be used in simple checks. However, I'd recommend you to use the second way, i.e. using The fastest JSON Schema Validator.
var Ajv = require('ajv'),
ajv = new Ajv({logger: console}),
schema = {
"properties": {
"expireDate": {
"type": ["string","null"]
}
}
};
var jsonData = JSON.parse(responseBody);
pm.test('All expire_date contains string', function() {
for (i = 0; i < jsonData.length; i++) {
pm.expect(ajv.validate(schema, {expireDate: jsonData[i].expire_date})).to.be.true;
}
});
If you'd like you check this in multiple requests, put schema at collection level.
I have a list of user uuids in myContactsUuids array, using forEach() method to loop through them and add user which is retrieved with new ChatEngine.User(uuid) function to myContacts array.
myContactsUuids = ["john_doe_001", "john_doe_005"];
// set a global array of users
myContacts = {};
myContactsUuids.forEach(function(uuid) {
myContacts[uuid] = new ChatEngine.User(uuid);
});
Now trying to rewrite this to do essentially the same, but have additional data nested under each uuid and pass that as JSON object with user uuid as string in ChatEngine.User() function.
I have user data now like this, though can format in in any way.
myContactsUuids = {"john_doe_001":{"username":"John Doe","avatar_url":"http://someurl"},"john_doe_003":{"username":"Another John Doe","avatar_url":"http://someurl"}};
and ChatEngine.User(uuid,data) function where uuid is user uuid string and data json object so for e.g. user in loop look like this :
new $scope.ChatEngine.User("john_doe_001", {"username":"John Doe","avatar_url":"http://someurl"});
Just not sure what would be the best way to write loop for this and pass needed data to it, and then add retrieved user to array like did in simplified function. Perhaps I could do that using each(), but not sure how to correctly.
#Ele solution works, just need result array to be in format like this:
{ "john_doe_001":{"uuid":"john_doe_001","data":{"username":"John Doe","avatar_url":"http://someurl"}},"john_doe_003":{"uuid":"john_doe_003","data":{"username":"Another John Doe","avatar_url":"http://someurl"}} }
You can use the function Object.entries along with the function map
var result = Object.entries(myContactsUuids)
.map(([uuid, data]) => ({ [uuid]: new $scope.ChatEngine.User(uuid, data) }));
Example snippet:
var ChatEngine = {
User: function(uuid, data) {
this.uuid = uuid;
this.data = data;
}
}
var myContactsUuids = {"john_doe_001":{"username":"John Doe","avatar_url":"http://someurl"},"john_doe_003":{"username":"Another John Doe","avatar_url":"http://someurl"}};
var result = Object.entries(myContactsUuids)
.map(([uuid, data]) => ({ [uuid]: new ChatEngine.User(uuid, data) }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Using the function reduce to build the desired output:
{
"john_doe_1": {
"data": {"2": 1}
},
"john_doe_2": {
"data": {"a": 1}
}
}
var ChatEngine = {
User: function(uuid, data) {
this.uuid = uuid;
this.data = data;
}
}
var myContactsUuids = {"john_doe_001":{"username":"John Doe","avatar_url":"http://someurl"},"john_doe_003":{"username":"Another John Doe","avatar_url":"http://someurl"}},
result = Object.entries(myContactsUuids)
.reduce((a, [uuid, data]) => {
a[uuid] = new ChatEngine.User(uuid, data);
return a;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have a JavaScript object with the following properties
{
name:"Request",
data:[1,2,3,4,5,6,7,8,9,10,11,12]
},
{
name:"Waiting",
data:[1,2,3,4,5,6,7,8,9,10,11,12]
}
I have a list that has status (name) and month ( data:[])
First I want to validate whether the object with the name eg "Request" exists.
If it does not exist, create a new object.
{ name:'Request', data:[]}.
If the name already exists, it will check if the month exists in the object array "data". If there is no month in the array you must enter it.
{name:'Request', data:[1]}
I'm not entirely sure what you want to do if everything is successful, but the code below should satisfy the requirements you have provided.
var objs = [{
name:"Request",
data:[1,2,3,4,5,6,7,8,9,10,11,12]
},
{
name:"Waiting",
data:[1,2,3,4,5,6,7,8,9,10,11,12]
}];
var month = 25;
var request = null;
// Validate whether the object with the name eg "Request" exists.
var requestExists = objs.some(function (elem) {
if (elem.name === "Request") {
request = elem;
return true;
}
});
//If it does not exist, create a new object. { name:'Request', data:[]}.
if(!requestExists) {
objs.push({name: "Request", data: []});
}
else {
// If the name does exist, check if the month exists in the "data" array.
// If there is no month in the array you must enter it. {name:'Request', data:[1]}
if (request.data.indexOf(month) === -1) {
request.data.push(month);
}
}
console.log(request);
console.log(objs);
var list = [{
name:"Waiting",
data:[1,2,3,4,5,6,7,8,9,10,11,12]
}]
var request = list.filter(status => status.name == "Request");
if (request.length === 0) list.push({name : "Request", data : [1]});
else if (request[0].data.length === 0) request[0].data.push(1);
console.log(list)
Since with the OP's example a data structure has to be checked and repaired whilst following some rules, a more generic approach should look for kind of a specific sanitizer function that can be both operated by itself (providing the key manually) but also run as a reduce method, if it comes to counterchecking the data structure against more than one data item key (like "Request" as it got provided with the OP's original example).
A solution then most probably will be close to the next provided one.
The Array API Documentation of the Mozilla Developer Network does provide polyfills for Array.isArray and Array.prototype.findIndex
function sanitizeDataList(list, key) {
var
indexOfDataItem = list.findIndex(function (dataItem) {
return (dataItem.name === key);
}),
dataItem = list[indexOfDataItem];
if (!dataItem) {
list.push(dataItem = {
name: key
});
}
if (!Array.isArray(dataItem.data) || (dataItem.data.length <= 0)) {
dataItem.data = [1];
}
return list;
}
var
dataList = [{
name: "Request_A",
data: [1,2,3,4,5,6,7,8,9,10,11,12]
}, {
name: "Waiting_A",
data: [1,2,3,4,5,6,7,8,9,10,11,12]
}/*, {
name: "Request_B",
data: [1,2,3,4,5,6,7,8,9,10,11,12]
}*/, {
name: "Waiting_B",
data: []
}, {
name: "Request_C"
}, {
name: "Waiting_C",
data: [1]
}],
dataItemKeyList = ["Request_A", "Waiting_A", "Request_B", "Waiting_B", "Request_C", "Waiting_C"];
dataList = dataItemKeyList.reduce(sanitizeDataList, dataList);
console.log(dataList);
.as-console-wrapper { max-height: 100%!important; top: 0; }
An easy solution is to use indexOf. You would need for it to be in a variable.
var myarray = [{
name:"Request",
data:[1,2,3,4,5,6,7,8,9,10,11,12]
},
{name:"Waiting",
data:[1,2,3,4,5,6,7,8,9,10,11,12]
}];
var myreturn = false;
for(i = 0; myarray.length > i; i++){
var data = myarray[i];
if(data.name == "Request"){
myreturn = true;
}
if (myreturn === true){
alert('sweet now do some code');
break;
}
}
My initial thought was incorrect, but using a For loop will work.
I have a filter to convert content with id into user name. For instance, it converts Thank you #id-3124324 ! into Thank you #Jack !.
var filter = function (content) {
var re = /\s#(id\-\d+)\s/g;
var matches = [];
var lastMatch = re.exec(content);
while (lastMatch !== null) {
matches.push(lastMatch[1]); // uid being mentioned
lastMatch = re.exec(content);
}
// TODO: query user name from matched id
// replace id with user name
// fake usernames here
var usernames = ['Random Name'];
for (var i = 0; i < usernames.length; ++i) {
content = content.replace(new RegExp(matches[i], 'g'), usernames[i]);
}
return content;
};
Vue.filter('username', filter);
But in my case, usernames should be achieved with AJAX of a query with id. How should I do it?
Anything you can do with a filter you can do with a computed. In Vue 2.0, there won't be filters, so you'll need to use computeds instead.
Fetching data asynchronously into a computed is a somewhat messy problem, which is why there is the vue-async-computed plugin. The difficulty is that the computed has to return a value synchronously, when it hasn't finished fetching data.
The solution is to have the computed depend on a cache of fetched data. If the needed value isn't in the cache, the computed kicks off the async process to fetch it and returns some placeholder value. When the process adds a value to the cache, the computed should notice the change in the cache and return the complete value.
In the demo below, I had to make an auxiliary variable, trigger, which I reference in the computed just so I know there's been an update. The fetch process increments trigger, which triggers the computed to re-evaluate. The computed doesn't notice when values are added to or updated in decodedIds. There may be a better way to deal with that. (Using async computed should make it a non-issue.)
vm = new Vue({
el: 'body',
data: {
messages: [
'Thank you #id-3124324!'
],
decodedIds: {},
trigger: 0
},
computed: {
decodedMessages: function() {
return this.messages.map((m) => this.decode(m, this.trigger));
}
},
methods: {
decode: function(msg) {
var re = /#(id\-\d+)/g;
var matches = msg.match(re);
for (const i in matches) {
const p1 = matches[i].substr(1);
if (!(p1 in this.decodedIds)) {
// Indicate name is loading
this.decodedIds[p1] = '(...)';
// Mock up fetching data
setTimeout(() => {
this.decodedIds[p1] = 'some name';
++this.trigger;
}, 500);
}
}
return msg.replace(re, (m, p1) => this.decodedIds[p1]);
}
}
});
setTimeout(() => {
vm.messages.push('Added #id-12345 and #id-54321.');
}, 1500);
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<div v-for="message in decodedMessages">
{{message}}
</div>