I have to parse a JSON file that has many objects but no structure to the file. It looks like this:
{"obj1": "john"}
{"obj2": "sally"}
{"obj3": "veronica"}
Each object is on on it's own there is no container. So when I open the file and try to iterate through it I get the error Unexpected token { in JSON
Aside from wrapping the objects in an array and then manually going through the whole file to add commas, how can I parse this?
If it's really one-object-per-line, it's fairly straightforward to take the string, break it into lines, and JSON.parse each line:
const str =
'{"obj1": "john"}\n' +
'{"obj2": "sally"}\n' +
'{"obj3": "veronica"}';
const array = str.split(/[\r\n]+/)
.map(entry => JSON.parse(entry));
console.log(array);
...but that's assuming it really is one object per line.
If you're reading the file, you don't have to start out with all in one string as above; just read line by line as Kevin B points out.
(Since you're using Node, I've happily used ES2015+ features above...)
If you assume each line of the input file is complete, self-standing JSON, then a split-into-lines-then-parse-each strategy works well.
But even if the data isn't limited to a single line, not all is lost. You can heuristically parse the file. It isn't hyper-efficient, but except for very large files you'll probably never know the difference:
function incrementallyParseJSON(filepath) {
var lines = fs.readFileSync(filepath)
.toString()
.split(/\n/g);
var result = [];
var [start, stop] = [0, 1];
while (stop <= lines.length) {
try {
var part = JSON.parse(lines.slice(start, stop).join('\n'));
result.push(part);
[start, stop] = [stop, stop+1];
} catch (e) {
stop += 1;
}
}
return result;
}
So if your file is:
{"obj1": "john"}
{"obj2": "sally",
"more": "other"}
{"obj3": "veronica"}
"something"
12
The result will be:
[ { obj1: 'john' },
{ obj2: 'sally', more: 'other' },
{ obj3: 'veronica' },
'something',
12 ]
Example:
function incrementallyParseJSON(str) {
var lines = str.split(/\n/g);
var result = [];
var [start, stop] = [0, 1];
while (stop <= lines.length) {
try {
var part = JSON.parse(lines.slice(start, stop).join('\n'));
result.push(part);
[start, stop] = [stop, stop+1];
} catch (e) {
stop += 1;
}
}
return result;
}
var str =
'{"obj1": "john"}\n' +
'{"obj2": "sally",\n' +
' "more": "other"}\n' +
'{"obj3": "veronica"}\n' +
'"something"\n' +
'12';
console.log(incrementallyParseJSON(str));
Related
This is my code:
const Discord = require('discord.js');
const client = new Discord.Client();
const TOKEN = "***********";
const PREFIX = "!";
client.on("ready", function () {
console.log("Ready!");
});
client.on("message", function (message) {
if (message.author.equals(client.user)) return;
if (!message.content.startsWith(PREFIX)) return;
var args = message.content.substring(PREFIX.length).split(" ");
switch (args[0]) {
case "rules":
var _embed = new Discord.RichEmbed()
.setTitle("Ruleset")
.addField("Where is my order?", "Theres only one proper way to recive an order and help. Its a command .ticket")
.addField("Why AZATEJ is such a bitch?", "If my status is 'dont disturb' and hue is way more red than green it means I have a reason to do so, im not a dick, but i recive a shitload of messages on daily route with stupid quiestions.")
.addField("Dont ask stupid questions", "Stupid doesnt mean basic, we are up to help you but before you'll contact anyone read twice explanation documents and use a ticket.")
.setColor(0x00FFFF)
.setFooter("This message is coool !")
.setThumbnail(message.author.avatarURL);
message.channel.send(_embed);
break;
case "spotify":
var uID = message.author.id;
for (let i = 0; i < ftpr.buyers.length; i++) {
if (uID === ftpr.buyers[i].id) {
var _embed = new Discord.RichEmbed()
.setTitle("Spotify")
.addField("Username", "testsda#yahoo.com")
.addField("Password", "ithastobe8")
.setColor(0x00FFFF)
.setFooter("Sincerely, LajgaardMoneyService")
.setThumbnail(message.author.avatarURL);
message.author.send(_embed);
console.log(message.author.username + "(" + JSON.stringify(ftpr.buyers[i].id) + ") Just used the command !spotify");
break;
}
else {
message.channel.send(message.author + "You haven't got a valid subscription. This command is locked until a new one is obtained!");
break;
}
}
break;
}
});
client.on('guildMemberAdd', function(member) {
console.log("User " + member.id + " has joined the server!");
//var role = member.guild.roles.find("name", "Google!");
var myRole = member.guild.roles.find("name", "Google!");
member.addRole(myRole);
});
client.login(TOKEN);
This is the JSON file:
{
"buyers": [
{
"id": "1331499609509724162"
},
{
"id": "181336616164392960"
},
{
"id": "266389854122672128"
}
]
}
When the bot is running and im changing one of the ID's the check function in case "spotify": is still using the old id. I do not want to restart the program every time the json file updates as it should be running 24/7. I have tried const fs = require("fs"); method but it gave me this error: TypeError: Cannot read property 'buyers' of undefined json
Sincerely, Oscar
const fs = require("fs"); just loads the module. Put that at the top of your file.
To read the json file each time you need to check user IDs (inefficient, but should get things working), put this at the top of your spotify case:
case "spotify":
var yourjsonfile = fs.readFileSync("yourjsonfile.json");
var ftpr = JSON.parse(yourjsonfile);
var uID = message.author.id;
for (let i = 0; i < ftpr.buyers.length; i++) {
if (uID === ftpr.buyers[i].id) {
Again, this is very inefficient - you are reloading the file every time you need to check it, and it uses readFileSync(), which blocks until the file is read (it is better to utilize node's asynchronous features). So as the JSON file grows larger, this will run slower. But at that point you probably need a database or some other mechanism for persisting and querying your data.
I am using script.google.com to create a custom connector that can read CSV data from drive.google.com and send the data to Googles data studio.
When running the connector and inserting a simple table inside the data studio, I receive a simple that the request could not be processed because of an server error. The error id is changing every time I "re-publish" the script.
This is
function getData(request) {
var dataSchema = [];
request.fields.forEach(function(field) {
for (var i = 0; i < csvDataSchema.length; i++) {
if (csvDataSchema[i].name === field.name) {
dataSchema.push(csvDataSchema[i]);
break;
}
}
});
csvFile = UrlFetchApp.fetch("https://drive.google.com/uc?export=download&id=" + request.configParams.documentId);
var csvData = Utilities.parseCsv(csvFile);
var data = [];
csvData.forEach(function(row) {
data.push({
values: row
});
});
console.log( {
schema: dataSchema,
rows: data
} );
return {
schema: dataSchema,
rows: data
};
};
This is the csvDataSchema:
var csvDataSchema = [
{
name: 'date',
label: 'Date',
dataType: 'STRING',
semantics: {
conceptType: 'DIMENSION'
}
},
{
name: 'nanoseconds',
label: 'nanoseconds',
dataType: 'NUMBER',
semantics: {
"isReaggregatable": true,
conceptType: 'METRIC'
}
},{
name: 'size',
label: 'Size of Testfile in MByte',
dataType: 'STRING',
semantics: {
"isReaggregatable": false,
conceptType: 'DIMENSION'
}
}
];
And this is the result of the getData function, stringified:
{"schema":[{"name":"date","label":"Date","dataType":"STRING","semantics":{"conceptType":"DIMENSION"}},{"name":"size","label":"Size of Testfile in MByte","dataType":"STRING","semantics":{"isReaggregatable":false,"conceptType":"DIMENSION"}}],"rows":[{"values":["2017-05-23",123,"1"]},{"values":["2017-05-23",123,"1"]}]}
It perfectly fits to the reference. I am providing more information, but following the tutorial it should work, anyways.
Those are the fields provided in request:
And this is what getDate returns:
So, what I am wondering first is: Why is there a random error id? And what could be wrong with my script?
You should only return fields/columns included in request. Currently, data contains all fields that are in csvFile. Depending on your chart element in your dashboard, request will most likely contain only a subset of your full schema. See example implementation at the Data Studio Open Source repo.
If this does not solve the problem, you should setup error handing and check if the error is occurring at any specific line.
#Minhaz Kazi gave the missing hint:
As I did not "dynamically" filled the response object in getData, I always returned all three columns.
With my code above the only thing I had to do is adding the third column as a dimension or a metric.
So I changed my code to dynamically return the columns so it will fit to the response. For this I had to implement an function that will transform the CSV-data into an object.
This is the getData() function now:
function getData(request) {
var url = "https://drive.google.com/uc?export=download&id="
+ request.configParams.documentId;
var csvFile = UrlFetchApp.fetch(url);
var csvData = Utilities.parseCsv(csvFile);
var sourceData = csvToObject(csvData);
var data = [];
sourceData.forEach(function(row) {
var values = [];
dataSchema.forEach(function(field) {
switch(field.name) {
case 'date':
values.push(row.date);
break;
case 'nanoseconds':
values.push(row.nanoseconds);
break;
case 'size':
values.push(row.size);
break;
default:
values.push('');
}
});
data.push({
values: values
});
});
return {
schema: dataSchema,
rows: data
};
};}
And this is the function to convert the CSV data to an object:
function csvToObject(array) {
var headers = array[0];
var jsonData = [];
for ( var i = 1, length = array.length; i < length; i++ )
{
var row = array[i];
var data = {};
for ( var x = 0; x < row.length; x++ )
{
data[headers[x]] = row[x];
}
jsonData.push(data);
}
return jsonData;
}
(it's based on a so-solution from here, I modified it to fit my source CSV data)
I'm using Kettle PDI 6.0 running on Windows Server 2012. I need to use the Modified Java Script Value to handle on Json object. I try something like this:
var jsondata = JSON.parse(result);
And get that:
"TypeError: Cannot find function parse in object test value test value test value test value test value test value test value test value test value test value. (script#3)"
I already try to looking for a solution on google, but not looks like that. I think that can be something wrong with my installation.
Note: I already try to use the command:
import java.util.*;
But that command is not recognized (Is not marked in bold).
I get:
missing ; before statement (script#2)
Maybe the Java functions not available.
I made my own function to resolve the problem. I will post here to help who has the same problem. If anyone want to help to solve the initial problem, I am still interested.
You can paste the code bellow on your "Modified Java Script Value" step after receive the Json response from service or get that on file. Note that you need to change the name of variables that you want to find on Json.
Result field is a Json Value.
//Script here
function findInArray(myValue, myArray){
var myResult='';
if(myArray.indexOf(myValue) > -1){
myResult = true;
} else {
myResult = false;
}
return myResult;
}
function getAttributeValue(Atribute, Object)
{
start = indexOf(Object,Atribute);
for (i= start; i < Object.length; i++)
{
if (substr(Object,i,1) == ":")
{
start_value = i+1;
break;
}
}
for (i= start_value; i < Object.length; i++)
{
end_value = i;
if (substr(Object,i,1) == ",")
{
break;
}
}
AttributeValue = replace(substr(Object, start_value, end_value-start_value),'"','');
if (indexOf(AttributeValue, "null") >= 0)
{
AttributeValue = null;
}
return AttributeValue ;
}
// Recupera Status
if (findInArray("status",result))
{
var status = getAttributeValue("status", result);
}
else
{
var status = "";
}
// Recupera _ID
if (findInArray("_id",result))
{
var mandrill_id = getAttributeValue("_id", result);
}
else
{
var mandrill_id = "";
}
// Recupera reject_reason
if (findInArray("reject_reason",result))
{
var reject_reason = replace(getAttributeValue("reject_reason", result),"}","");
}
else
{
var reject_reason = "";
}
yes, the parse json function is not available on the ex4 ecmascript of js rhino engine build in kettle, but you can handle json in kettle using eval.
var resultObj = eval('('+result+')');
//now you can iterate the foo elements of result original json
for(i=0;i< resultObj.length;i++){
Alert('foo number ' + i ' value = ' + resultObj[i].foo);
}
This is not javascript for the browser so eval is perfectly safe.
I have just recentely used AngularJS to "convert" a data structure I had in pure SVG format into JSON format.
Now, I want to store such a structure in a MongoDB database to start finally connecting some components of the MEAN stack together and start seeing some things working! Basically, I have the following code inside a Webstorm AngularJS project:
JS:
var app = angular.module('app', []);
var RectangleDim=30;
app.controller('MainCtrl', function($scope) {
$scope.graph = {'width': 5000, 'height': 5000};
$scope.circles = [
/* JSON.parse("{\"x\": 85, \"y\": 20, \"r\":15}"),
{"x": 20, "y": 60, "r":20},
{"x": 18, "y": 10, "r":40} */
];
$scope.draw=function(val)
{
// val = document.getElementById("NumQuest").value;
return JSON.parse('{\"cx\":'+val+', "cy": 20, "r":30}');
// $scope.circles.push(JSON.parse('{\"x\":'+val+', "y": 220, "r":30}'));
};
$scope.rectangles = [
// {'x':220, 'y':220, 'width' : 300, 'height' : 100},
// {'x':520, 'y':220, 'width' : 10, 'height' : 10},
];
$scope.DrawRect=function(xpos,ypos) {
return JSON.parse('{\"x\":' + xpos + ', \"y\":' + ypos + ', \"width\":' + RectangleDim + ', \"height\":' + RectangleDim+ '}');
};
$scope.Debug=function(desiredNo){
desiredNo=document.getElementById("NumQuest").value;
for(var i = 0;i < RectangleDim*desiredNo+desiredNo;i++){
$scope.rectangles.push($scope.DrawRect(i+RectangleDim+1,40));
}
};
$scope.DrawLineOdd=function(desiredNo,lineNo,pozY){
var pozX = lineNo*RectangleDim;
var aux = 2*Math.floor(Math.sqrt(desiredNo))-1-2*lineNo;
for (var j = 0; j < aux; j++) {
$scope.rectangles.push($scope.DrawRect(pozX, pozY));//$scope.DrawRect(pozX, pozY);
pozX += RectangleDim;
}
//return aux;
};
$scope.DrawMatrixPerfectProgression=function(desiredNo) {
desiredNo=document.getElementById("NumQuest").value;
var line=0;
var pozy=0;
while(line<Math.floor(Math.sqrt(desiredNo))) {
$scope.DrawLineOdd(desiredNo, line, pozy);
//document.getElementById("val").innerHTML = teste;
line += 1;
pozy+=RectangleDim;
}
//document.getElementById('tablePrint').innerHTML = finalTable;
};
$scope.DrawLineEven=function(desiredNo, lineNo, pozY){
var pozX = lineNo*RectangleDim;
//var pozY = lineno*20;
var aux = 2*Math.floor(Math.sqrt(desiredNo))-2*lineNo;
for (var j = 0; j < aux; j++) {
$scope.rectangles.push($scope.DrawRect(pozX, pozY));
pozX += RectangleDim;
}
//return aux;
};
$scope.DrawMatrixEvenProgression=function(desiredNo) {
desiredNo=document.getElementById("NumQuest").value;
var line=0;
var pozy=0;
while(line<Math.floor((Math.sqrt(4*desiredNo+1)-1)/2)) {
$scope.DrawLineEven(desiredNo, line, pozy);
//document.getElementById("val").innerHTML = teste;
line += 1;
pozy+=RectangleDim;
}
//document.getElementById('tablePrint').innerHTML = finalTable;
};
$scope.AddExtraRectangles=function(desiredNo) {
desiredNo = document.getElementById("NumQuest").value;
var arg1 = desiredNo - ( Math.floor(Math.sqrt(desiredNo))*Math.floor(Math.sqrt(desiredNo)));
var arg2 = desiredNo-(Math.floor((Math.sqrt(4*desiredNo+1)-1)/2)*Math.floor((Math.sqrt(4*desiredNo+1)-1)/2))-Math.floor((Math.sqrt(4*desiredNo+1)-1)/2);
var OptimalLeftOver = Math.min( arg1 ,arg2 );
//We add two rectangles per row: one at the beginning one at the end
//we start with the row below the first one
var line;
var pozy;
var pozx1, pozx2;
var nRectLine_i;
if(OptimalLeftOver===arg1){
line=1;//1st line is skipped
pozy=RectangleDim;
pozx1 = 0;
while(OptimalLeftOver>0) {
nRectLine_i = 2* Math.floor(Math.sqrt(desiredNo))-1-2*line;
pozx2 = (line-1)*RectangleDim+RectangleDim*(nRectLine_i+1);//pozx1+nRectLine_i+2*RectangleDim;
$scope.rectangles.push($scope.DrawRect(pozx1,pozy));
OptimalLeftOver-=1;
if(OptimalLeftOver>0) {
$scope.rectangles.push($scope.DrawRect(pozx2, pozy));
OptimalLeftOver -= 1;
}
//document.getElementById("val").innerHTML = teste;
line += 1;
pozy+=RectangleDim;
pozx1=RectangleDim*line - RectangleDim;
}
//document.getElementById('tablePrint').innerHTML = finalTable;
}
else {
line=1;//1st line is skipped
pozy=RectangleDim;
pozx1 = 0;
while(OptimalLeftOver>0) {
nRectLine_i = 2* Math.floor(Math.sqrt(desiredNo))-2*line;
pozx2 = RectangleDim*(line-1)+RectangleDim*(nRectLine_i+1);//pozx1+nRectLine_i+2*RectangleDim;
$scope.rectangles.push($scope.DrawRect(pozx1,pozy));
OptimalLeftOver-=1;
if(OptimalLeftOver>0) {
$scope.rectangles.push($scope.DrawRect(pozx2, pozy));
OptimalLeftOver -= 1;
}
//document.getElementById("val").innerHTML = teste;
line += 1;
pozy+=RectangleDim;
pozx1=RectangleDim*line - RectangleDim;
}
//document.getElementById('tablePrint').innerHTML = finalTable;
}
};
/* $scope.DrawMatrix=function(desiredNo)
{
/* Chooses optimal leftover number based on the progression formulas.
Attempts to minimize the work of the designer of the response form without
making too much assumptions
desiredNo = document.getElementById("NumQuest").value;
var arg1 = desiredNo - ( Math.floor(Math.sqrt(desiredNo))*Math.floor(Math.sqrt(desiredNo)));
var arg2 = desiredNo - (Math.floor((Math.sqrt(4*desiredNo+1)-1)/2)*Math.floor((Math.sqrt(4*desiredNo+1)-1)/2))-Math.floor((Math.sqrt(4*desiredNo+1)-1)/2);
var OptimalLeftOver = Math.min( arg1 ,arg2 );
document.getElementById("val").innerHTML = 'There are '+OptimalLeftOver+' questions missing!'+ arg1+ '___'+arg2;
console.log(arg1);
if(OptimalLeftOver===arg1){
DrawMatrixPerfectProgression(desiredNo);
AddExtraRectangles(desiredNo);
}
else {
DrawMatrixEvenProgression(desiredNo);
AddExtraRectangles(desiredNo);
}
}; */
}
);
angular.bootstrap(document.getElementById('body'), ["app"]);
The relevant part of the code is the $scope.rectangles array which contains the JSON.parse of the strings representing my data structure on the html side and that structure in JSON (or JSON parsed or whatever) is what I want to save in the MongoDB database...How can I do that? The HTML relevant part is just like this:
HTML:
<p><button ng-click="DrawMatrixEvenProgression(NumQuest)">Draw</button></p>
<svg ng-attr-height="{{graph.height}}" ng-attr-width="{{graph.width}}">
<rect ng-repeat="rect in rectangles"
ng-attr-x="{{rect.x}}"
ng-attr-y="{{rect.y}}"
ng-attr-width="{{rect.width}}"
ng-attr-height="{{rect.height}}">
</rect>
</svg>
Any help will be appreciated... Can I start by adding more files to that project to handle the database and then things will be linked together?
Like adding stuff to handle the mongoose and the connections?
Thanks in advance!
Because Angular is a front-end framework. So to communicate with database (in this case MongoDB) you need to have application on the server-side to handle this and I suggest you to use Node.js and Mongoose as a MongoDB driver.
Node.js
Mongoose
Come back to Angular, you can create Angular service or factory and let the them talk to your server with service like $http or $resource.
Angular service documentation
Example for angular service
angular.module('app')
.factory('RectangleService', function($http){
return {
create: create
}
function create(rectangle){
// make http request to the server
return $http({
url: 'API_URL',
method: 'GET',
params: rectangle
});
}
});
After you create your service you have to inject it to your controller and
you may create some function to your $scope to talk with service like this
app.controller('MainCtrl', function($scope, RectangleService) { // <-- Inject service to controller
// your controller code
$scope.createRectangle = function(rectangle){
RectangleService.create(rectangle);
}
});
You can map createRectangle function to directive like ng-click and pass your json data as a parameter
Because I don't know what server-side language you can use, so I don't come with an example for Node.js & Mongoose
Hope this can help :)
My sample json is
"multiList": [
{
"my_key" : "this is my key"
},
{
"my_text_box": "This is my text box"
},
]
How do I convert this to
{"my_key" : "this is my key"},
{my_text_box": "This is my text box"},
dynamically?
using jquery
Your question doesn't make sense. Are you asking to convert to two separate objects? A string representation of those two objects? Something else? I can do the first two:
var objOne = json.multiList[0];
var objTwo = json.multiList[1];
var objStr = JSON.stringify(json.multiList[0]) + ', '
+ JSON.stringify(json.multiList[1]);
If you want to add all of the separate properties into one object, you can just extend another object in a loop.
var obj = {};
json.multiList.forEach(function (elem) {
for (k in elem) {
if (elem.hasOwnProperty(k)) {
obj[k] = elem[k];
}
}
});
http://jsfiddle.net/ExplosionPIlls/t2xyd/
This makes no consideration for the overriding of properties in obj.