JavaScript/GSON: Access JSON references dynamically over object graph (circular references) - javascript

I had the problem, to serialize my Java objects through Google GSON, because of several circular references. All my tries ended up in a StackOverflowException, because GSON is not able to handle those circular references.
As a solution, I found following GraphAdapterBuilder:
http://code.google.com/p/google-gson/source/browse/trunk/extras/src/main/java/com/google/gson/graph/GraphAdapterBuilder.java?r=1170
Example:
https://groups.google.com/forum/#!topic/google-gson/z2Ax5T1kb2M
{
"0x1": {
"name": "Google",
"employees": [
"0x2",
"0x3"
]
},
"0x2": {
"name": "Jesse",
"company": "0x1"
},
"0x3": {
"name": "Joel",
"company": "0x1"
}
}
This is working very well, but I am still not able to access the reference values (0xn) dynamically over the object graph like:
alert(0x3.company.name); --> Should print "Google", but I only receive undefined
Is it somehow possible to achieve this?
Maybe with a custom JSON.parse(ajaxResponse, function(key,value) {} function which replaces the variable with the referenced object tree?

For future users, refer to this answer which uses GraphAdapterBuilder: https://stackoverflow.com/a/10046134/1547266

!! UPDATE, BETTER SOLUTION !!
If you can switch your library, just use FlexJson >>> http://flexjson.sourceforge.net/.
I solved my problem with an own JSON parser:
"ref" is "0x[n]" in the original GraphAdapterBuilder
Source:
$.ajax({
type: "POST",
url: "controller/ajaxmethod.htm",
data: { "var1": var1, "var2":var2},
success: function(response){
var jsonObject = parseGraphGSON(response, 0);
...
},
error: function(e){
alert('Error: ' + e.status);
}
});
function parseGraphGSON(gsonResponse, recursionLevel) {
var maxRecursionDepth = 2;
var jsonObject = JSON.parse(gsonResponse, function(key, value) {
if (typeof value === 'string') {
if (value.indexOf("ref") == 0) {
if (recursionLevel < maxRecursionDepth) {
return parseGraphGSON(gsonResponse, recursionLevel + 1)[value];
} else {
return JSON.parse(gsonResponse)[value];
}
}
}
return value;
});
return jsonObject;
}

Related

How to set/get/save data in session storage?

here is example of my data that I get after ajax call response is successful obj.DATA looks like this:
{
"A43D": {
"FIRSTNAME": "Mike",
"EMAIL": "mjohns#gmail.com",
"LASTNAME": "Johns"
},
"4E83": {
"FIRSTNAME": "Steve",
"EMAIL": "scook#gmail.com",
"LASTNAME": "Cook"
}
}
$.ajax({
type: 'POST',
url: 'AjaxFunctions.cfc?method=getCustomers',
data: formData,
dataType: 'json'
}).done(function(obj){
console.log(obj.DATA);
sessionStorage.setItem("customersData", JSON.stringify(obj.DATA));
}).fail(function(jqXHR, textStatus, errorThrown){
alert('Error: '+errorThrown);
});
Then I dump sessionStorage in console.log(sessionStorage) and I see this:
{
"customersData": "{\"A43D\":{\"FIRSTNAME\":\"Mike\",\"EMAIL\":\"mjohnes#gmail.com\",\"LASTNAME\":\"Johnes\"},\"4E83\":{\"FIRSTNAME\":\"Steve\",\"EMAIL\":\"scook#gmail.com\",\"LASTNAME\":\"Cook\"}}"
}
So I was trying next:
sessionData = sessionStorage.hasOwnProperty(customersData[recordID]) ? JSON.parse(sessionStorage.getItem(customersData[recordID])) : null;
This is in the function where I just pass record id and then try to access that record in session storage. If I try to console.log(sessionData) all I see is null. I'm wondering how I can access specific key in customersData object inside of the session storage? Also how I would insert/edit record in sessionStorage customersData object?
each time you try to save/load something for the localStorage/sessionStorage and you know it is a json object, always stringify/parse it depending on the case.
here you have your code fixed to work.
NOTE: I tried to create a snippet but it didn't work because we can not access to the sessionStorage of a sandbox.
NOTE2: always check what data are you going to parse, if the record doesnt exist on the storage, it will return null.
var data = {
"A43D": {
"FIRSTNAME": "Mike",
"EMAIL": "mjohns#gmail.com",
"LASTNAME": "Johns"
},
"4E83": {
"FIRSTNAME": "Steve",
"EMAIL": "scook#gmail.com",
"LASTNAME": "Cook"
}
}
//here we save the item in the sessionStorage.
sessionStorage.setItem("customersData", JSON.stringify(data));
//now we retrieve the object again, but in a string form.
var customersDataString = sessionStorage.getItem("customersData");
console.log(customersDataString);
//to get the object we have to parse it.
var customersData = JSON.parse(customersDataString);
console.log(customersData);
Here's how I'd approach this,
by creating some customersStorage Object with methods like get, set and add
jsFiddle demo
var customersStorage = {
// Get all or single customer by passing an ID
get: function( id ) {
var parsed = JSON.parse(sessionStorage.customersData || "{}");
return id ? parsed[id] : parsed;
},
// Set all
set: function( obj ) {
return sessionStorage.customersData = JSON.stringify(obj || {});
},
// Add single customer and store
add: function( id, obj ) {
var all = this.get();
// Make sure customer does not exists already;
if(all.hasOwnProperty(id)) return console.warn(id+ " exists!");
all[id] = obj;
return this.set(all);
}
};
// Let's play!
// Store All
customersStorage.set({
"A43D": {
"FIRSTNAME": "Mike",
"EMAIL": "mjohns#gmail.com",
"LASTNAME": "Johns"
},
"4E83": {
"FIRSTNAME": "Steve",
"EMAIL": "scook#gmail.com",
"LASTNAME": "Cook"
}
});
// Get single
console.log( customersStorage.get("4E83") );
// Add new
customersStorage.add("AAA0", {
"FIRSTNAME": "Espresso",
"LASTNAME": "Coffee",
"EMAIL": "espresso#coffee.com"
});
// Get all
console.log( customersStorage.get() );
Furthermore, to make your session handling object more "client" agnostic I'd expand it to handle even more data by namespace:
var ObjectStorage = function(nsp, useSession) {
var stg = (useSession ? sessionStorage : localStorage);
return {
// Get all or single customer by passing an ID
get: function(id) {
var parsed = JSON.parse(stg[nsp] || "{}");
return id ? parsed[id] : parsed;
},
// Set all
set: function(obj) {
return stg[nsp] = JSON.stringify(obj || {});
},
// Add one to all
add: function(prop, val) {
var all = this.get();
// Make sure property does not exists already;
if (all.hasOwnProperty(prop)) return console.warn(prop + " exists!");
all[prop] = val;
return this.set(all);
}
}
};
// Let's play!
// Create instance. Use true to use sessionStorage (instead of localStorage)
var Customers = new ObjectStorage("customersData", true);
// now you can use .add(), .get(), .set() on your Customers Object

Get the closest match in a string

I have a json file with some domains which I try to match in a foreach object function.
JSON-File:
"calendar-google": {
"domainMatch": "calendar.google",
"icon": "assets/icons/google-calendar.png"
},
"inbox-google": {
"domainMatch": "inbox.google",
"icon": "assets/icons/google-gmail.png"
},
"github": {
"domainMatch": "github",
"icon": "assets/icons/github.png"
},
"google": {
"domainMatch": "google",
"icon": "assets/icons/google-google.png"
},
"trello": {
"domainMatch": "trello",
"icon": "assets/icons/trello.png"
},
"tweetdeck": {
"domainMatch": "tweetdeck.twitter",
"icon": "assets/icons/twitter.png"
},
"twitter": {
"domainMatch": "twitter",
"icon": "assets/icons/twitter.png"
},
"youtube": {
"domainMatch": "youtube",
"icon": "assets/icons/youtube.png"
}
Now I want to check if my url in my local storage does match with one of these "domainMatch" properties.
JavaScript:
$.getJSON("domainList.json", function (data) {
Object.getOwnPropertyNames(data).forEach(function(val, idx, array) {
var domainMatch = data[val].domainMatch
if(localStorage.getItem("favouriteSite")) {
var sites = JSON.parse(localStorage.getItem("favouriteSite"));
// Lets think firstlink is "calender.google.com"
var firstLink = sites.site1.link;
if(firstLink.includes(domainMatch)){
// 2 Results but I want only that
// result which is near!!
}
}
});
You see in the comment, that I not want the match "google" and "calender.google". I want only the nearest match from both (calender.google in this case).
How can I get the nearest match of a string?
When I did not write something detailed enough, then please write it but I think you should understand what I mean.
Greetings
Julian
Use Array.prototype.some() function to return the result immediately on the first match:
$.getJSON("domainList.json", function (data) {
var matched = Object.keys(data).some(function(k) {
var domainMatch = data[k].domainMatch;
if (localStorage.getItem("favouriteSite")) {
var sites = JSON.parse(localStorage.getItem("favouriteSite"));
// Lets think firstlink is "calender.google.com"
var firstLink = sites.site1.link;
return firstLink.includes(domainMatch);
}
};
});
Instead of forEach, use find to get the first match or null if nothing is matched like this:
$.getJSON("domainList.json", function(data) {
var sites = JSON.parse(localStorage.getItem("favouriteSite")); // this should be out here (so you won't fetch it evertime)
var firstLink = sites.site1.link;
var val = Object.keys(data).find(function(key) { // find will stop at the first found item (Object.keys) is better since it's just a plain object)
var domainMatch = data[key].domainMatch;
return firstLink.includes(domainMatch);
});
console.log(val); // val will be the first matched key ('github', 'google', ...) or null if nothing is matched
/***************** EDIT: **********************/
if(val) { // if we found something
var obj = data[val]; // get the object (because val is the key)
var icon = obj.icon; // now obj is an object from the array data (you can access its icon property like this)
}
});

Search for a related json data

How can i find data that is related to the already known data?
( I'm a newb. )
For example here is my json :
[
{ "id": "1", "log": "1","pass": "1111" },
{ "id": 2, "log": "2","pass": "2222" },
{ "id": 3, "log": "3","pass": "3333" }
]
Now i know that "log" is 1 and i want to find out the data "pass" that is related to it.
i've tried to do it so :
The POST request comes with log and pass data , i search the .json file for the same log value and if there is the same data then i search for related pass
fs.readFile("file.json", "utf8", function (err, data) {
var jsonFileArr = [];
jsonFileArr = JSON.parse(data); // Parse .json objekts
var log = loginData.log; // The 'log' data that comes with POST request
/* Search through .json file for the same data*/
var gibtLog = jsonFileArr.some(function (obj) {
return obj.log == log;
});
if (gotLog) { // If there is the same 'log'
var pass = loginData.pass; // The 'pass' data that comes with POST request
var gotPass = jsonFileArr.some(function (obj) {
// How to change this part ?
return obj.pass == pass;
});
}
else
console.log("error");
});
The problem is that when i use
var gotPass = jsonFileArr.some(function (obj) {
return obj.pass == pass;
});
it searches through the whole .json file and not through only one objekt.
Your main problem is that .some() returns a boolean, whether any of the elements match your predicate or not, but not the element itself.
You want .find() (which will find and return the first element matching the predicate):
const myItem = myArray.find(item => item.log === "1"); // the first matching item
console.log(myItem.pass); // "1111"
Note that it is possible for .find() to not find anything, in which case it returns undefined.
The .some() method returns a boolean that just tells you whether there is at least one item in the array that matches the criteria, it doesn't return the matching item(s). Try .filter() instead:
var jsonFileArr = JSON.parse(data);
var log = loginData.log;
var matchingItems = jsonFileArr.filter(function (obj) {
return obj.log == log;
});
if (matchingItems.length > 0) { // Was at least 1 found?
var pass = matchingItems[0].pass; // The 'pass' data that comes with the first match
} else
console.log("error"); // no matches
Using ES6 Array#find is probably the easiest, but you could also do (among other things)
const x = [{
"id": "1",
"log": "1",
"pass": "1111"
}, {
"id": 2,
"log": "2",
"pass": "2222"
}, {
"id": 3,
"log": "3",
"pass": "3333"
}];
let myItem;
for (let item of x) {
if (item.log === '1') {
myItem = item;
break;
}
}
console.log(myItem);

MongoDB projection to return child objects as columns

I am new to MongoDB and I have a mongodb document like this
{
"_id": 1,
"title": "abc123",
"author": {
"last": "zzz",
"first": "aaa",
"address": {
"street": "stvalue",
"city": "NewYork"
}
}
}
I am using MongoDB Java client and trying to convert the above document as
{
"_id": 1,
"title": "abc123",
"author_last": "zzz",
"author_first": "aaa",
"author_address_street": "stvalue",
"author_address_city": "New York"
}
Is this possible using MongoDB Java client using MongoDB query without modifying the result in Java.
Update
Since it is not possible to convert to required format in MongoQuery, I have to change my mind and add some Java code to get the required format.
It's not really a matter of which "client" or "driver library" you use, as much as the only way to do this is via the .aggregate() and $project to "alter the fields" in response. It is your most practical way of doing so:
db.collection.aggregate([
{ "$project": {
"title": 1,
"author_last": "$author.last",
"author_first": "$author.first",
"author_address_street": "$author.address.street",
"author_address_city": "$author.address.city"
}}
])
Very easy to translate into any language, but shows you the basic principles.
Very Java(esque) then the syntax becomes (in basic driver):
BasicDBObject project = new BasicDBObject("$project",
new BasicDBObject( "title", 1 )
.append( "author_last", "$author.last" )
.append( "author_first", "$author.first" )
.append( "author_address_street", "$author.address.street" ),
.append( "author_address_city", "$author.address.city" )
);
collection.aggregate(project);
If you cannot just specify the keys in this way then your only real option on the server is mapReduce. Much in the same way as above, it might be better to do this in the client response:
db.collection.mapReduce(
function() {
var item = {};
var id = this._id;
delete this._id;
var obj = this;
Object.keys(obj).forEach(function(key) {
if ( typeof(obj[key]) == "object" ) {
Object.keys(obj[key]).forEach(function(subKey) {
if ( typeof(obj[key][subkey] == "object" ) {
Object.keys(obj[key][subKey]).forEach(function(subSub) {
item[key + "_" + subKey + "_" + subSub] = obj[key][subKey][subSub];
});
} else {
item[key + "_" + subKey] = obj[key][subKey];
}
});
} else {
item[key] = obj[key];
}
});
emit( id, item );
},
function() {}, // no reduction required
{ "out": { "inline": 1 } }
)
I think that makes sense, but I'm just typing it so possible syntax problems, but the idea should be relevant. You might also need something more complex depending on your actual data.
Point is, that you need the JavaScript execution to "traverse" your document on the server to use this method.
Otherwise, just do the same in code since you are iterating a cursor anyway.
Here is an Java implementation which will convert all child objects into an HashMap
private List<Map<String,String>> mapReduce(AggregationOutput output){
List<Map<String,String>> out = new ArrayList<Map<String, String>>();
for (DBObject result : output.results()) {
Map<String,String> map = ConvertDBObjectToMap(result, "");
out.add(map);
}
return out;
}
private Map<String,String> ConvertDBObjectToMap(DBObject result, String parent)
{
String prefix = parent == "" ? "" : parent + "_";
Map<String,String> out = new HashMap<String, String>();
String[] keys = result.keySet().toArray(new String[result.keySet().size()]);
for(String key : keys)
{
Object obj = result.get(key);
if(obj instanceof BasicDBObject)
{
out.putAll(ConvertDBObjectToMap((BasicDBObject) obj, prefix + key));
}
else if(obj instanceof String)
{
out.put(prefix + key,(String)obj);
}
else if(obj instanceof Date){
out.put(prefix + key,obj.toString());
}
else{
out.put(prefix + key,obj.toString());
}
}
return out;
}

IndexedDB and Javascript: JSON and objects misunderstanding

I'm trying to obtain information from a JSON file download to the client through AJAX and I'm getting different results depending on the JSON format and I don't know how to fix the one with problem.
First case:
The json files looks like:
[{"name": "nick",
"age": 28},
{"name": "katie",
"age": 32}]
My AJAX .done method looks like:
.done(
function(data) {
addObjectsDB (data, "people");
})
This method calls a second one that iterates through data and stored correctly each object into IndexedDB.
Second case:
Now I have a JSON file with different format:
[
{
"husband": {
"name": "Jhon",
"age": 23 },
"wife": {
"name": "Marie",
"age": 24 }
}
]
Now my .done() AJAX method iterates through data and add each person, husband or wife to an array which is then sent to the DB with the same method than the first case:
.done(
function(data) {
var people = [];
$(data).each(function (key, value){
people.push(value.husband);
people.push(value.wife);
});
addObjectsDB (people, "people");
})
In this case the insertion into the database fails, if for example, instead of adding value.husband to people array I just add value to people array the insertion works, but I need each person stored separated in the DB.
The addObjectsDB method is:
function addObjectsDB (data, collection) {
var objectStore = db.transaction(collection, "readwrite").objectStore(collection);
$.each (data, function (key, value) {
var request = objectStore.add(value);
});
}
As I said the first case works perfectly but the second one inserts nothing and no error is showed...
I think the problem is that I don't understand javascript types adequately but I'm starting with it and I've spent a whole evening with it.
There's nothing wrong with your IDB code. Look for your answer in the code you haven't presented, particularily the AJAX response (is your JSON parsed the way you think it is?)
Be sure to attach event listeners for the error event. I'm positive that if your IDB "inserts nothing" then in fact it's not true that "no error is showed" and rather no error is seen due to callback mismanagement.
Here's a working implementation, modified from a previous answer I've given on this tag. This implementation doesn't have the uniqueness constraints you've put on your schema on purpose: it shows that your looping is fine. The entries below all look good.
var db_name = 'SO_22977915',
store_name = 'people';
$.ajax({
url: '/echo/json/',
type: 'post',
dataType: 'json',
data: {
json: JSON.stringify({
case1: [{
"name": "nick",
"age": 28
}, {
"name": "katie",
"age": 32
}],
case2: [{
"husband": {
"name": "Jhon",
"age": 23
},
"wife": {
"name": "Marie",
"age": 24
}
}]
})
},
success: function (data) {
var request,
upgrade = false,
doTx = function (db, entry) {
addData(db, entry, function () {
getData(db);
});
},
getData = function (db) {
db.transaction([store_name], "readonly").objectStore(store_name).openCursor(IDBKeyRange.lowerBound(0)).onsuccess = function (event) {
var cursor = event.target.result;
if (null !== cursor) {
console.log("entry", cursor.value);
cursor.continue();
}
};
},
addData = function (db, entry, finished) {
console.log('adding', entry);
var tx = db.transaction([store_name], "readwrite"),
people = [];
tx.addEventListener('complete', function (e) {
finished();
});
$.each(entry.case1, function (key, value) {
tx.objectStore(store_name).add(value);
});
$(entry.case2).each(function (key, value){
people.push(value.husband);
people.push(value.wife);
});
$.each(people, function (key, value) {
tx.objectStore(store_name).add(value);
});
};
request = window.indexedDB.open(db_name);
request.oncomplete = function (event) {
if (upgrade) {
doTx(request.result, data);
}
};
request.onsuccess = function (event) {
if (!upgrade) {
doTx(request.result, data);
}
};
request.onupgradeneeded = function (event) {
var db = event.target.result;
db.createObjectStore(store_name, {
keyPath: null,
autoIncrement: true
});
}
}
});
A cursor and console.log shows all entries as being added:

Categories

Resources