Parse Java object to Javascript - javascript

In Spring MVC and Jackson I have a really big Java object that I parse to a JSON string myBigJSONString (~1MB) and inline it into my HTML.
Then in my Javascript I do a JSON.parse(myBigJSONString) and everything works dandy.
However if I were to inline an syntactically correct Javascript object into my HTML and not a string representation thereof, my Javascript wouldn't have to read this huge string and parse it. It would already be parsed.
Question: How do I create a Javascript object from my Java object and inline it directly without going through a JSON string?
EDIT:
This is how my finished HTML looks right now:
var staticDataString = '[{\"propertyA\":\"valueA\"}, {\"propertyB\":\"valueB\"}]';
var staticData = JSON.parse(staticDataString);
This is how i want it to look
var staticData = [{propertyA:"valueA"}, {propertyB:"valueB"}];

In all current browsers this should work:
<script>
var obj = <c:out value="${$myserlvetmapping.myjson}"></c:out>;
alert(obj.test);
</script>
Whearat this is the Spring-Handler:
model.addAttribute("myjson","{test:2}");
Finally this would be the browsers sourcecode:
var obj = {test:2};
alert(obj.test);
And the alert reports 2.
About the creation of the JSON i suggest to use the "adapter-pattern", this means a lot of hand-written-code.
Why do you need the hand-written-adapter? Assuming you have crosswise referenced objects in java like This:
class Man {
Wife wife;
}
class Wife {
Man man;
}
Man joe = new Man();
Wife ann = new Wife();
joe.wife = ann;
ann.man = joe;
Your json would be
{man:{
name:'joe',
wife: {
name: 'ann',
man: {
name: 'joe',
wife: {
name: 'ann',
man: {
name: 'joe',
wife: {
.....
}
}
}
}
}
}}
To prevent recursion you can only use the Adapter-Pattern. This would work:
public final class ManJSONAdapter {
private final Man man;
public ManJSONAdapter(Man man){
this.man = man;
}
public String toJSON(){
String result="{";
if (man != null) {
result += "name:";
if (man.name == null){
result += "undefined"
} else {
result += "'" + StringEscapeUtils.ESCAPE_ECMASCRIPT.translate(man.name) +"'";
}
result += ",wife:";
if (man.wife == null) {
result += "undefined";
} else {
...
}
}
result += "}";
return result;
}
}

Related

Apply template string dynamically in JavaScript [duplicate]

I have a string with say: My Name is %NAME% and my age is %AGE%.
%XXX% are placeholders. We need to substitute values there from an object.
Object looks like: {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"}
I need to parse the object and replace the string with corresponding values. So that final output will be:
My Name is Mike and my age is 26.
The whole thing has to be done either using pure javascript or jquery.
The requirements of the original question clearly couldn't benefit from string interpolation, as it seems like it's a runtime processing of arbitrary replacement keys.
However, if you just had to do string interpolation, you can use:
const str = `My name is ${replacements.name} and my age is ${replacements.age}.`
Note the backticks delimiting the string, they are required.
For an answer suiting the particular OP's requirement, you could use String.prototype.replace() for the replacements.
The following code will handle all matches and not touch ones without a replacement (so long as your replacement values are all strings, if not, see below).
var replacements = {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"},
str = 'My Name is %NAME% and my age is %AGE%.';
str = str.replace(/%\w+%/g, function(all) {
return replacements[all] || all;
});
jsFiddle.
If some of your replacements are not strings, be sure they exists in the object first. If you have a format like the example, i.e. wrapped in percentage signs, you can use the in operator to achieve this.
jsFiddle.
However, if your format doesn't have a special format, i.e. any string, and your replacements object doesn't have a null prototype, use Object.prototype.hasOwnProperty(), unless you can guarantee that none of your potential replaced substrings will clash with property names on the prototype.
jsFiddle.
Otherwise, if your replacement string was 'hasOwnProperty', you would get a resultant messed up string.
jsFiddle.
As a side note, you should be called replacements an Object, not an Array.
How about using ES6 template literals?
var a = "cat";
var b = "fat";
console.log(`my ${a} is ${b}`); //notice back-ticked string
More about template literals...
Currently there is still no native solution in Javascript for this behavior. Tagged templates are something related, but don't solve it.
Here there is a refactor of alex's solution with an object for replacements.
The solution uses arrow functions and a similar syntax for the placeholders as the native Javascript interpolation in template literals ({} instead of %%). Also there is no need to include delimiters (%) in the names of the replacements.
There are two flavors (three with the update): descriptive, reduced, elegant reduced with groups.
Descriptive solution:
const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';
const replacements = {
name: 'Mike',
age: '26',
};
const string = stringWithPlaceholders.replace(
/{\w+}/g,
placeholderWithDelimiters => {
const placeholderWithoutDelimiters = placeholderWithDelimiters.substring(
1,
placeholderWithDelimiters.length - 1,
);
const stringReplacement = replacements[placeholderWithoutDelimiters] || placeholderWithDelimiters;
return stringReplacement;
},
);
console.log(string);
Reduced solution:
const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';
const replacements = {
name: 'Mike',
age: '26',
};
const string = stringWithPlaceholders.replace(/{\w+}/g, placeholder =>
replacements[placeholder.substring(1, placeholder.length - 1)] || placeholder
);
console.log(string);
UPDATE 2020-12-10
Elegant reduced solution with groups, as suggested by #Kade in the comments:
const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';
const replacements = {
name: 'Mike',
age: '26',
};
const string = stringWithPlaceholders.replace(
/{(\w+)}/g,
(placeholderWithDelimiters, placeholderWithoutDelimiters) =>
replacements[placeholderWithoutDelimiters] || placeholderWithDelimiters
);
console.log(string);
UPDATE 2021-01-21
Support empty string as a replacement, as suggested by #Jesper in the comments:
const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';
const replacements = {
name: 'Mike',
age: '',
};
const string = stringWithPlaceholders.replace(
/{(\w+)}/g,
(placeholderWithDelimiters, placeholderWithoutDelimiters) =>
replacements.hasOwnProperty(placeholderWithoutDelimiters) ?
replacements[placeholderWithoutDelimiters] : placeholderWithDelimiters
);
console.log(string);
You can use JQuery(jquery.validate.js) to make it work easily.
$.validator.format("My name is {0}, I'm {1} years old",["Bob","23"]);
Or if you want to use just that feature you can define that function and just use it like
function format(source, params) {
$.each(params,function (i, n) {
source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n);
})
return source;
}
alert(format("{0} is a {1}", ["Michael", "Guy"]));
credit to jquery.validate.js team
As with modern browser, placeholder is supported by new version of Chrome / Firefox, similar as the C style function printf().
Placeholders:
%s String.
%d,%i Integer number.
%f Floating point number.
%o Object hyperlink.
e.g.
console.log("generation 0:\t%f, %f, %f", a1a1, a1a2, a2a2);
BTW, to see the output:
In Chrome, use shortcut Ctrl + Shift + J or F12 to open developer tool.
In Firefox, use shortcut Ctrl + Shift + K or F12 to open developer tool.
#Update - nodejs support
Seems nodejs don't support %f, instead, could use %d in nodejs.
With %d number will be printed as floating number, not just integer.
Just use replace()
var values = {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"};
var substitutedString = "My Name is %NAME% and my age is %AGE%.".replace("%NAME%", $values["%NAME%"]).replace("%AGE%", $values["%AGE%"]);
You can use a custom replace function like this:
var str = "My Name is %NAME% and my age is %AGE%.";
var replaceData = {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"};
function substitute(str, data) {
var output = str.replace(/%[^%]+%/g, function(match) {
if (match in data) {
return(data[match]);
} else {
return("");
}
});
return(output);
}
var output = substitute(str, replaceData);
You can see it work here: http://jsfiddle.net/jfriend00/DyCwk/.
If you want to do something closer to console.log like replacing %s placeholders like in
>console.log("Hello %s how are you %s is everything %s?", "Loreto", "today", "allright")
>Hello Loreto how are you today is everything allright?
I wrote this
function log() {
var args = Array.prototype.slice.call(arguments);
var rep= args.slice(1, args.length);
var i=0;
var output = args[0].replace(/%s/g, function(match,idx) {
var subst=rep.slice(i, ++i);
return( subst );
});
return(output);
}
res=log("Hello %s how are you %s is everything %s?", "Loreto", "today", "allright");
document.getElementById("console").innerHTML=res;
<span id="console"/>
you will get
>log("Hello %s how are you %s is everything %s?", "Loreto", "today", "allright")
>"Hello Loreto how are you today is everything allright?"
UPDATE
I have added a simple variant as String.prototype useful when dealing with string transformations, here is it:
String.prototype.log = function() {
var args = Array.prototype.slice.call(arguments);
var rep= args.slice(0, args.length);
var i=0;
var output = this.replace(/%s|%d|%f|%#/g, function(match,idx) {
var subst=rep.slice(i, ++i);
return( subst );
});
return output;
}
In that case you will do
"Hello %s how are you %s is everything %s?".log("Loreto", "today", "allright")
"Hello Loreto how are you today is everything allright?"
Try this version here
This allows you to do exactly that
NPM: https://www.npmjs.com/package/stringinject
GitHub: https://github.com/tjcafferkey/stringinject
By doing the following:
var str = stringInject("My username is {username} on {platform}", { username: "tjcafferkey", platform: "GitHub" });
// My username is tjcafferkey on Git
I have written a code that lets you format string easily.
Use this function.
function format() {
if (arguments.length === 0) {
throw "No arguments";
}
const string = arguments[0];
const lst = string.split("{}");
if (lst.length !== arguments.length) {
throw "Placeholder format mismatched";
}
let string2 = "";
let off = 1;
for (let i = 0; i < lst.length; i++) {
if (off < arguments.length) {
string2 += lst[i] + arguments[off++]
} else {
string2 += lst[i]
}
}
return string2;
}
Example
format('My Name is {} and my age is {}', 'Mike', 26);
Output
My Name is Mike and my age is 26
Another solution if you're using node.js is StackExchange's own formatUnicorn utility function (https://www.npmjs.com/package/format-unicorn):
let x = {name:'jason', food:'pine cones'};
let s = '{name} enjoys a delicious bowl of {food}';
let formatted = x.formatUnicorn(s);
Also, a bit of an edge case, but if you aren't using Node but you do just happen to be writing a userscript for SE sites, then formatUnicorn will already be on the String prototype.
As a quick example:
var name = 'jack';
var age = 40;
console.log('%s is %d yrs old',name,age);
The output is:
jack is 40 yrs old
Here is another way of doing this by using es6 template literals dynamically at runtime.
const str = 'My name is ${name} and my age is ${age}.'
const obj = {name:'Simon', age:'33'}
const result = new Function('const {' + Object.keys(obj).join(',') + '} = this.obj;return `' + str + '`').call({obj})
document.body.innerHTML = result
const stringInject = (str = '', obj = {}) => {
let newStr = str;
Object.keys(obj).forEach((key) => {
let placeHolder = `#${key}#`;
if(newStr.includes(placeHolder)) {
newStr = newStr.replace(placeHolder, obj[key] || " ");
}
});
return newStr;
}
Input: stringInject("Hi #name#, How are you?", {name: "Ram"});
Output: "Hi Ram, How are you?"
ES6:
const strFormat = (str, ...args) => args.reduce((s, v) => s.replace('%s', v), str);
// Use it like:
const result = strFormat('%s is %s yrs old', 'name', 23);
Lots of good/similar answers here. I wanted the ability to easily get a nested key in an object (or perhaps some JSON data structure) for substitution, so I took the following simple approach:
const getKey = (d, path) => {
// path can be a string like 'key1.key2' or an iterable of keys
if (typeof(path) === 'string') {
path = path.split('.')
}
return path.reduce((x, y) => x[y], d)
}
const inject = (str, obj) => str.replace(/\${(.*?)}/g, (x,g)=> getKey(obj, g));
// Example
> const str = 'there are ${a} ways to ${b.c}'
undefined
> inject(str, {'a':'many', 'b': {'c': 'skin a cat'}})
'there are many ways to skin a cat'
Some inspiration from this and this.
This is a merged solution of Gerson Diniz and Shubham Vyas.
It is possible to pass a set of arguments or an object.
function strSwap(str) {
if (!str) return null;
let args = [];
for (let a of arguments)
args.push(a);
args.shift();
if (!args.length) return null;
// replacement by object - {{prop}}
if (!!(args[0].constructor && args[0].constructor.name.toLowerCase() === 'object')) {
for (let i in args[0]) {
let n = `{{${i}}}`;
str = str.includes(n) ? str.replaceAll(n, args[0][i] + '') : str;
}
}
// replacement by placeholders - %s
else {
str = args.reduce((s, v) => s.replace('%s', v), str);
}
return str;
}
// ---------------------
console.log(strSwap('Hello %s, my name is %s.', 'alice', 'bob'));
console.log(strSwap('Hello {{a}}, my name is {{b}}. Hello {{b}}.', {
a: 'alice',
b: 'bob'
}));

How to eval an object creation in javascript

I'm doing some exercises from school where is asking me to eval the class creation, I am given the html with several eval scripts. I'm supposed to create the javascript file to generate certain output.
This is the eval one:
try {
var output = eval('peter = new Person("Roger Roger")');
}
catch (err) {
var output = "[JavaScript Error] " + err.message;
}
if (output != '[Person "Roger Roger"]') {
error = true;
}
the expected output should be:
[Person "Roger Roger"]
but I'm getting
[object Object]
I've been looking around, but there isn't much information on how to evaluate the class creation.
Your help is very much appreciated.
When you coerce eval's return value to a string you get the default toString implementation.
You need to create a Person.prototype.toString method so coercion returns the output you're looking for, e.g.,
Person.prototype.toString = function() {
return "[Person " + this.name + "]";
}
var output = "" + eval('peter = new Person("Roger Roger")');
console.log(output);
> [Person Roger Roger]
OTOH, it's not super-clear to me what the assignment ultimately is.

Get object in array with underscore.js

I have a function with a rather convoluted object in this format:
function getNBATeamsESPNByAbbrev(abbrev)
{
var json = {
"sports":[
{
"name":"basketball",
"id":40,
"uid":"s:40",
"leagues":[
{
"name":"National Basketball Assoc.",
"abbreviation":"nba",
"id":46,
"uid":"s:40~l:46",
"groupId":7,
"shortName":"NBA",
"teams":[
{
"id":1,
"uid":"s:40~l:46~t:1",
"location":"Atlanta",
"name":"Hawks",
"abbreviation":"ATL",
},
{
"id":2,
"uid":"s:40~l:46~t:2",
"location":"Boston",
"name":"Celtics",
"abbreviation":"BOS",
"color":"006532",
},
]
}
]
}
],
"resultsOffset":0,
"resultsLimit":50,
"resultsCount":30,
"timestamp":"2014-03-22T23:42:43Z",
"status":"success"
}
obj = _.find(json.sports[0].leagues[0].teams, function(obj) { return obj.abbreviation == abbrev })
if (obj !== undefined)
{
var team = new Object();
team.abbrev = abbrev;
team.location = obj.location;
team.nickname = obj.name;
return team;
}
}
It can be easier seen at this example JSFiddle. So I have the team abbreviation, and I want to pull back the team object as a whole (this is a stripped down version, leaving only the necessary details). This seems to work fine. However, another case has arisen, one in which I need to pull back the team object based on its location + " " + name. So I tried to do the same thing using underscore.js, passing in the parameter name, and changing the predicate in ._find to return obj.location + " " + obj.name == name. For example, I'd pass in Atalnta Hawks as name and expect to return the relevant team object. Here's a very similar JSFiddle demonstrating the change. But, now it can't seem to find the team object I want to pull. Is it because such a string concatenation isn't allowed in underscore.js, or is there something stupid I'm missing?
Line 50, you have:
team.abbrev = obj.abbrev;
and it should be
team.abbrev = obj.abbreviation;

json nested lookup using string based key in javascript

With a javascript json object like this:
var data = {
blog : {
title: "my blog",
logo: "blah.jpg",
},
posts : [
{
title: "test post",
content: "<p>testing posts</p><br><p>some html</p>"
},
]
}
var lookup = "blog.title" //this gets generated from a template file
Now I know you can do something like, but these don't quite do what I need:
console.log(data['blog']); //works
console.log(data.blog.title); //works
console.log(data['blog']['title']); //works, but i dont know the depth of the lookup
But I need to be able to do something like the code below because I can't hardcode the structure, it gets generated and stored in lookup each time. Do I have to build this functionality using string cutting and recursion?? I really hope not
console.log(data['blog.title']); //does not work
console.log(data[lookup]); //does not work
EDIT....
Okay, possibly found a workaround. I don't know if this is safe or recommended practice, so comments on that would be great. Or alternative methods. So combining this with the code above.
var evaltest = "var asdf ="+JSON.stringify(data)+";\n"
evaltest += "asdf."+lookup
console.log(eval(evaltest)) //returns correctly "my blog" as expected
You could use dottie https://www.npmjs.org/package/dottie, which allows you to deep traverse an object using strings
var values = {
some: {
nested: {
key: 'foobar';
}
}
}
dottie.get(values, 'some.nested.key'); // returns 'foobar'
dottie.get(values, 'some.undefined.key'); // returns undefined
you could use:
data['blog']['title']
I've experimented with a couple ways of doing this including eval and using a dictionary lookup with switch(exp.length). This is the final version (comments stripped) I created:
var deepRead = function (data, expression) {
var exp = expression.split('.'), retVal;
do {
retVal = (retVal || data)[exp.shift()] || false;
} while (retVal !== false && exp.length);
return retVal || false;
};
//example usage
data = {
a1: { b1: "hello" },
a2: { b2: { c2: "world" } }
}
deepRead(data, "a1.b1") => "hello"
deepRead(data, "a2.b2.c2") => "world"
deepRead(data, "a1.b2") => false
deepRead(data, "a1.b2.c2.any.random.number.of.non-existant.properties") => false
Here's the Gist with full commenting: gist.github.com/jeff-mccoy/9700352. I use this to loop over several thousand items and have had no issues with deep-nested data. Also, I'm not wrapping in try/catch anymore due to the (small) performance hit: jsPerf.

Get a JSON result based on a Ilist<int> in MVC

I need to build a string like "1-2-3-4-5", from an IList< int > returned by an MVC Action.
Action:
public virtual JsonResult AdvancedSearch(AdAdvancedSearchViewModel asViewModel)
{
IList<int> adIds = new List<int>();
try
{
var asDto = Mapper.Map<AdAdvancedSearchViewModel, AdAdvancedSearchDto>(asViewModel);
adIds = _adService.AdvancedSearch(asDto);
}
catch
{
adIds = null;
}
return Json(adIds);
}
Javascript function that processes the result:
function onAdAdvancedSearchSuccess(jsonAdListIds)
{
$("#adAdvancedSearchListForm #ids").val(jsonAdListIds);
}
The problem is that I get a string like this "[1,2,3,4]" in the "#adAdvancedSearchListForm #ids" HTML input, and I need to get "1-2-3-4", any idea?
Thanks.
If you want to do it at the client side, simply iterate throught the result array and build the string you want.
$.getJSON("yourURL", { Name: "John", Loc: "2pm" },function(result){
var str="";
$.each(result,function(i,item){
str=str+"-"+item;
});
alert(str);
});
Assuming Name and Loc are the properties of your viewmodel.
If you want to do it on the server side, you may use String.Join method to build the string representation you want. You may need to update the return type of your action method.
public string AdvancedSearch(AdAdvancedSearchViewModel asViewModel)
{
List<int> adIds = new List<int>();
//fill the list
var resutlStr= String.Join("-",adIds.ToArray());
return resutlStr;
}
I prefer to keep my action method returns JSON result instead of this string represetnation because an action method which returns JSON can be used in many places compared to this concrete string return implementation.
AJAX is returning an array which is expected since you are converting a list.
Try parsing the array into the string:
var r = jsonAdListIds[0];
for (var i = 1; i < jsonAdListIds.length; i++) {
r += '-' + jsonAdListIds[i];
}

Categories