I want to view SPARQL query results as JSON objects. For example, I have a RDF database where I have parents graph which includes children, relatives and their information. Is there a way to view them as JSON objects like
"Parents": {
"names": "some names"
"children":[
{"child1": {
"name": name
}}
]
.....
}
How can I achieve this? All suggestions are welcome. Thanks
SPARQL provides "application/sparql-results+json" as its document content-type for query solutions for consumption by applications that understand JSON.
In recent times I've come to realize that this aspect of SPARQL is generally understood thereby creating artificial friction for "Web Developers" who work with tools that support JSON as the default content-type for structured data.
Anyway, we recently released a HTML5, CSS, and Javascript based Single Page Application that demonstrates what's possible with SPARQL when you put its "application/results+json" query solution content-type to use. Naturally, it also provides a solution for understanding how to process JSON returned from SPARQL.
How the form works.
Code Snippet regarding JSON object handling
/*
Dynamic Table for processing JSON Structured Data (via "application/sparql-results+json" document content type)
that enables INSERT to be handled via a 3-tuple subject, predicate, object graph (relation) while query results
are handled via an N-Tuple structured table (relation).
*/
if (data.results.bindings.length > 0){
var table = tabCheckTable("dbmsTableID", "fsTableID") ; // creates table for header
var header = table.createTHead(); // creates empty tHead
var headRow = header.insertRow(0); // inserts row into tHead
var bindings = data.results.bindings;
for (var col = 0; col < data.head.vars.length; col++) { // for each column
// console.log("col = " + col)
var headCell = headRow.insertCell(col); // inserts new cell at position i in thead
headCell.innerHTML = "<b>" + data.head.vars[col] + "</b>"; // adds bold text to thead cell
}
for (i in bindings) {
// console.log("i = " + i)
var curr = 0 ; // curr is used to keep track of correct cell position
var binding = bindings[i];
var bodyRow = table.insertRow(-1); // create new row
for (n in binding) {
// console.log("n = " + n)
// console.log("curr = " + curr)
var bodyCell = bodyRow.insertCell(curr); // create new cell in row
bodyCell.innerHTML = tableFormat(binding[n].value); // set value of cell
curr += 1 ;
}
}
}
else{
throw new Error("No Data Returned");
console.log("No data returned by query");
}
})
} catch(e) {
console.error('Query Failed:', e) ;
}
}
Links
"Deceptively Simple" Data Entry Form -- this is also View-Source friendly re. all the code that drives the app
Github Repository
Related
I was working with a table in HTML and I have the following doubt: I am taking the data from a JSON array: {"key":["1","2","3"],"values":["4","5","6"]} and when trying to insert them into the table instead of putting each data in a column all the data is put in the first column, the JS code that I am using to insert the data in the table is:
var idtabla = document.getElementById("idtabla")
idtabla.innerHTML += window.location.href.split("/tabla/")[1]
function getJson(){
var id = window.location.href.split("/tabla/")[1]
var table = document.getElementById("table")
$.get( `/json`, (data) => {
if(data.error){
idtabla.innerHTML = 404 + ", tabla no encontrada"
} else {
var keys = data.key.forEach(element => {
table.innerHTML += `<tr><td>${element}</td>`
});
var values = data.values.forEach(element => {
table.innerHTML += `<td>${element}</td></tr>`
});
}
})
}
and the result it gives me is this:
How could it be solved? Thanks a lot.
There are two problems.
First, you can't add partial HTML to innerHTML like that. Whenever you assign to innerHTML, it parses the result completely. If there are any unclosed tags, they're closed automatically. If you want to build the HTML incrementally, you should do it by appending to a string, then assign the completed string to innerHTML.
Second, you're appending all the values all the values after all the keys, so they won't be in matching rows. You need to append both the key and value in the same loop. Since forEach() passes the array index to the callback function, you can use that to index into the other array.
let tableHTML = '';
data.keys.forEach((key, i) => {
tableHTML += `<tr><td>${key}</td><td>${data.values[i]}</td></tr>`;
});
table.innerHTML = tableHTML;
This is a follow-up question derivated from How to solve error when adding big number of tables
With the code below, I get the following message when, for 500 tables. BUt it works fine for 200 for example.
Exception: Service Documents failed while accessing document with id
The error happens on line 22, inside de if body = DocumentApp.getActiveDocument().getBody();
You also have the table template id to try, but here is an image
Image Table Template
function RequirementTemplate_Copy() {
var templatedoc = DocumentApp.openById("1oJt02MfOIQPFptdWCwDpj5j-zFdO_Wrq-I48mUq9I-w");
return templatedoc.getBody().getChild(1).copy()
}
function insertSpecification_withSection(){
// Retuns a Table Template Copied from another Document
reqTableItem = RequirementTemplate_Copy();
var body = DocumentApp.getActiveDocument().getBody();
// Creates X number of separated tables from the template
for (var i = 1; i < 501; i++){
table = reqTableItem.copy().replaceText("#Title#",String(i))
body.appendTable(table);
if((i % 100) === 0) {
DocumentApp.getActiveDocument().saveAndClose();
body = DocumentApp.getActiveDocument().getBody()
}
}
}
It looks that the error message isn't related to the number of tables to be inserted because it occurs before adding the tables.
Just wait a bit an try again. If the problem persist try your code using a different account if the code runs on the second account it's very possible that you first account exceeded a limit... there are some limits to prevent abuse that aren't published and that might change without any announcement.
Using the fix suggested for the code from my answer to the previous question and changing the number for iteration limit to 1000 and 2000 works fine
The following screenshot shows the result for 1000
Here is the code used for the tests
function insertSpecification_withSection(){
startTime = new Date()
console.log("Starting Function... ");
// Retuns a Table Template Copied from another Document
reqTableItem = RequirementTemplate_Copy();
var body = DocumentApp.getActiveDocument().getBody();
// Creates X number of separated tables from the template
for (var i = 0; i < 2000; i++){
table = body.appendTable(reqTableItem.copy());
// if((i % 100) === 0) {
// DocumentApp.getActiveDocument().saveAndClose();
// }
//
}
endTime = new Date();
timeDiff = endTime - startTime;
console.log("Ending Function..."+ timeDiff + " ms");
}
function RequirementTemplate_Copy() {
//---------------------------------------------------------------------------------------------------------------------------------------------------
var ReqTableID = PropertiesService.getDocumentProperties().getProperty('ReqTableID');
try{
var templatedoc = DocumentApp.openById(ReqTableID);
} catch (error) {
DocumentApp.getUi().alert("Could not find the document. Confirm it was not deleted and that anyone have read access with the link.");
//Logger.log("Document not accessible", ReqTableID)
}
var reqTableItem = templatedoc.getChild(1).copy();
//---------------------------------------------------------------------------------------------------------------------------------------------------
return reqTableItem
}
function setReqTableID(){
PropertiesService.getDocumentProperties().setProperty('ReqTableID', '1NS9nOb3qEBrqkcAQ3H83OhTJ4fxeySOQx7yM4vKSFu0')
}
Here is what I have tried.
I have tried dot notation and quotes. None of them seem to work. What exactly could be the problem?
var clientsList;
Client.find({}, function(err, clients) {
clientsList = clients;
// I have 10 clients in the loop
for (var j = 0; j < clientsList.length; j++) {
var x = clientsList[j];
x.count = "20";
//x["count"] = "20";
console.log(x);
}
});
Existing Client object
{"name":"abcd", "email":"abc#gmail.com"}
I'm unable to add the count key value pair into the client object. What could be the problem?
I suspect the object you're being given by Client.find has extensions prevented (it's sealed or frozen).
You can create a new object with the original's own, enumerable properties plus your count property using ES2018 spread notation:
x = {...x, count: "20"};
...or ES2015's Object.assign:
x = Object.assign({}, x, {count: "20"});
If the array is also sealed or frozen, you can copy it and its objects like this:
clientList = clients.map(client => {
return {...client, count: "20"}; // Or with `Object.assign`
});
or even with the concise form of the arrow function:
clientList = clients.map(client => ({...client, count: "20"}));
Side note: This pattern:
var clientsList;
Client.find({}, function(err, clients) {
clientsList = clients;
// ...
});
...often suggests that you intend to use clientsList in code following the Client.find call and expect it to have the result from that call's callback. See this question's answers for why, if you're doing that, it doesn't work.
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'm creating a document dynamically with some heading structure
doc = DocumentApp.create("My Document");
doc.appendParagraph("Main").setHeading(DocumentApp.ParagraphHeading.HEADING1);
var section = doc.appendParagraph("Section 1");
section.setHeading(DocumentApp.ParagraphHeading.HEADING2);
I can open it online, insert Table of contents and can access directly to "Section 1" by url like:
https://docs.google.com/document/d/1aA...FQ/edit#heading=h.41bpnx2ug57j
The question is: How I can get similar url/id to the "Section 1" in the code at run time and use it later as a link?
If I can't - is there any way to set something like anchor/bookmark and get it's url?
Thanks!
Starting to test Google Apps in depth, I had issues with the limited features related to the management of table of contents. I bumped into the code you proposed and used it as a starting point to write my own function to format a table of content:
- applying proper headings styles,
- numeroting the different parts.
I hope this would help some of you improving Google Docs templates:
/**
* Used to properly format the Table of Content object
*/
function formatToc() {
//Define variables
var level1 = 0;
var level2 = 0;
// Define custom paragraph styles.
var style1 = {};
style1[DocumentApp.Attribute.FONT_FAMILY] = DocumentApp.FontFamily.ARIAL;
style1[DocumentApp.Attribute.FONT_SIZE] = 18;
style1[DocumentApp.Attribute.BOLD] = true;
style1[DocumentApp.Attribute.FOREGROUND_COLOR] = '#ff0000';
var style2 = {};
style2[DocumentApp.Attribute.FONT_FAMILY] = DocumentApp.FontFamily.ARIAL;
style2[DocumentApp.Attribute.FONT_SIZE] = 14;
style2[DocumentApp.Attribute.BOLD] = true;
style2[DocumentApp.Attribute.FOREGROUND_COLOR] = '#007cb0';
// Search document's body for the table of contents (assuming there is one and only one).
var toc = doc.getBody().findElement(DocumentApp.ElementType.TABLE_OF_CONTENTS).getElement().asTableOfContents();
//Loop all the table of contents to apply new formating
for (var i = 0; i < toc.getNumChildren(); i++) {
//Search document's body for corresponding paragraph & retrieve heading
var searchText = toc.getChild(i).getText();
for (var j=0; j<doc.getBody().getNumChildren(); j++) {
var par = doc.getBody().getChild(j);
if (par.getType() == DocumentApp.ElementType.LIST_ITEM) {
var searchcomp = par.getText();
if (par.getText() == searchText) {
// Found corresponding paragrapg and update headingtype.
var heading = par.getHeading();
var level = par.getNestingLevel();
}
}
}
//Insert Paragraph number before text
if (level==0) {
level1++;
level2=0;
toc.getChild(i).editAsText().insertText(0,level1+". ");
}
if (level==1) {
level2++;
toc.getChild(i).editAsText().insertText(0,level1+"."+level2+". ");
}
//Apply style corresponding to heading
if (heading == DocumentApp.ParagraphHeading.HEADING1) {
toc.getChild(i).setAttributes(style1);
}
if (heading == DocumentApp.ParagraphHeading.HEADING2) {
toc.getChild(i).setAttributes(style2);
}
}
}
Now it is impossible to get a document part (section, paragraph, etc) link without having a TOC. Also there is no way to manage bookmarks from a GAS. There is an issue on the issue tracker. You can star the issue to promote it.
There is a workaround by using a TOC. The following code shows how to get URL from a TOC. It works only if the TOC exists, if to delete it, the links do not work anymore.
function testTOC() {
var doc = DocumentApp.openById('here is doc id');
for (var i = 0; i < doc.getNumChildren(); i++) {
var p = doc.getChild(i);
if (p.getType() == DocumentApp.ElementType.TABLE_OF_CONTENTS) {
var toc = p.asTableOfContents();
for (var ti = 0; ti < toc.getNumChildren(); ti++) {
var itemToc = toc.getChild(ti).asParagraph().getChild(0).asText();
var itemText = itemToc.getText();
var itemUrl = itemToc.getLinkUrl();
}
break;
}
}
}
The function iterates all document parts, finds the 1st TOC, iterates it and the variables itemText and itemUrl contain a TOC item text and URL. The URLs have #heading=h.uuj3ymgjhlie format.
Since the time the accepted answer was written, the ability to manage bookmarks inside Google Apps Script code was introduced. So it is possible to get a similar URL, though not the same exact URL as in example. You can manually insert a bookmark at the section heading, and use that bookmark to link to the section heading. It seems that for the purposes of the question, it will suffice. Here is some sample code (including slight modifications of code from question):
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
body.appendParagraph("Main").setHeading(DocumentApp.ParagraphHeading.HEADING1);
var section = body.appendParagraph("Section 1");
section.setHeading(DocumentApp.ParagraphHeading.HEADING2);
// create and position bookmark
var sectionPos = doc.newPosition(section, 0);
var sectionBookmark = doc.addBookmark(sectionPos);
// add a link to the section heading
var paragraph = body.appendParagraph("");
paragraph.appendText("Now we add a ");
paragraph.appendText("link to the section heading").setLinkUrl('#bookmark=' + sectionBookmark.getId());
paragraph.appendText(".");
Is it imperative that the document is a native Google docs type (ie. application/vnd.google-apps.document)?
If you stored the document as text/html you would have much greater control over how you assemble the document and how you expose it, eg with anchors.