Nashorn/Rhino to convert a string from Java to Javascript - javascript

I'm using Play Framework and I have a .java Controller file in which I obtain an array of strings. I want to pass this Java array into an html file that will use Javascript in order to plot the data using Flot Charts. This data "transfer" is done in the render. It is something like this:
String[] array = new String[list.size()];
int i = 0;
for (Sample sample : list) {
array[i++] = sample.getContent();
}
render(array);
But then when I'm unable to call this variable in the .html file inside the views folder. If I use ${array}, Firebug tells me that it does not recognize it as a valid JS String array. I've read that Rhino or Nashorn could do the trick, but I do not know if they are the best and simplest option. Any ideas? Thanks!

I'm not familiar with Play Framework but I'm doing similar stuff using SparkJava in both java and javascript (using Nashorn).
I would suggest to use Boon library to generate json: https://github.com/boonproject/boon.
Here's a small Nashorn snippet to get you up to speed, easily adaptable to java:
// 1st we create a factory to serialize json out
var jso = new org.boon.json.JsonSerializerFactory().create();
// 2nd we directly use boon on array variable. Boon supports out of the box many pure java objects
jso.serialize(o);
In your specific case, you'll need to configure Play output for that particular render as application/json and possibly use render(jso.serialize(o)); in place of the small snippet I gave.

Related

Looping through a list of Java objects in JavaScript with Thymeleaf

In a Spring Boot project, I have a list of users, in Java List<User>
I pass it from the Controller to the template, I am able to loop through this list using a HTML list ul but I am not able to do it in JavaScript:
<script layout:fragment="script" th:inline="javascript">
/*<![CDATA[*/
var users = /*[[${users}]]*/ [];
for (var i = 0; i < users.length; i++) {
console.log(i); // Obviously here I would like to access the User properties
}
/*]]>*/
</script>
I get this error:
Error during execution of processor
'org.thymeleaf.standard.processor.text.StandardTextInliningTextProcessor'
How do we loop through lists and access Java object properties in JavaScript with Thymeleaf?
Thanks.
EDIT: what I have discovered so far
My User class is a JPA entity with a Country property (Country is another JPA entity):
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "country_id")
private Country country;
If I pass a list of users without the country fetched the JavaScript works.
If I pass a list of countries and loop through (to test the Country class by itself), it works too.
If I pass a list of users where I set the country myself (not fetched from database) with the same values as the database contains, it works
If I retrieve the country from the database, then set it to the user, it fails.
So it seems like the problem is because the Country entity is created/mapped to the User a certain way by Spring Data/Hibernate that Thymeleaf can not deserialize it...
JavaScript natural templates
Have you got Jackson library in your classpath?
If you have it, It should work.
If you take a look the reference documentation it works as you are doing it:
An important thing to note regarding JavaScript inlining is that this
expression evaluation is intelligent and not limited to Strings.
Thymeleaf will correctly write in JavaScript syntax the following
kinds of objects:
Strings ...
That ${session.user} expression will evaluate to a User object, and Thymeleaf will correctly convert it to Javascript syntax
At least, other way If you want, is configure how the Serialization is done with an instance of StandardDialect:
The way this JavaScript serialization is done is by means of an
implementation of the
org.thymeleaf.standard.serializer.IStandardJavaScriptSerializer
interface, which can be configured at the instance of the
StandardDialect being used at the template engine.
As I've said before, you should have Jackson Library in your classpath (although the reference says it will have similar results I would add it):
The default implementation of this JS serialization mechanism will
look for the Jackson library in the classpath and, if present, will
use it. If not, it will apply a built-in serialization mechanism that
covers the needs of most scenarios and produces similar results (but
is less flexible).
Finally, try to log users content (console.log(users)) to see what is doing the serialization.
Java mapping
Other easier way is If you want to iterate a list of java objects yous could map it to a Json in Java (using a library like Jackson or Gson) and then add it as parameter to the thymeleaf template.
ObjectMapper mapper = new ObjectMapper();
String users = mapper.writeValueAsString(users);
model.addAttribute("users", users);
The result of the parameter will be in a JSON format.

Parsing Javascript Data Object in Java

Hello i need help in parsing an object that is being return to java. I am running some automated tests using selenium. In my test i am using the framework to call javascript functions and what i am doing is assigning what is passed back from the javascript function to a java object. The event object passed from javascript looks like this
{
data={
isFailover=false,
baseClip={
expression=full,
isAd=true,
connectionBitrate=0,
baseURL=null,
contentCustomData={
fw: titleId=null,
fw: advertiserId=null,
fw: category=null
},
overlays=[],
ratings=[]
}
}
}
I have an object called "responseToMediaIsAd" defined as
private static Object responseToMediaIsAd;
The data response is being outputted (above) comes when i do this
System.out.println(responseToMediaIsAd);
So its great that i get data back:-) The only problem now is HOW do i parse thru this data. do i have to convert it to json then parse that data?... Can someone tell me how to read the value of (say isFailover, and isAd), ie need to know how to step through and get values
I need to know how to use Java to get to this data that Javascript is returning. Thank you
Well, you can use the Jackson JSON parser library, or if you know regex, you can painfully roll a custom one with some crafted queries.

Populate Javascript array using Scala List in Play framework template

Ok so I have a template in Play! that receives a List as a parameter:
#(actions : List[RecipientAction])
RecipientAction is just a regular case class with a couple of fields. Within the template, I have a <script> tag where I want to use D3 to make a line chart. Inside the script I want to populate a JavaScript array with objects that contain the properties stored in RecipientAction in order to use them for my line chart later. I currently have this code:
testArray2=[];
for(var i=0; i < #actions.length;i++){
testArray2[i]= {};
testArray2[i].eventAt= #actions(i).eventAt.toString();
testArray2[i].action= #actions(i).action.id;
}
When i run it, i get the error "not found: value i". This is because i is a client side variable while actions is a server side variable, so scala cannot find i. What would be the best way to work around this and successfully populate the array?
You need to create a JSON serializer for your RecipientAction, then you'll just be able to print the list as JSON in the template. Say it looks something like this..
import play.api.libs.json._
case class RecipientAction(id: Int, description: String)
object RecipientAction {
// Define a `Writes` for `RecipientAction`
implicit val writes: Writes[RecipientAction] = Json.writes[RecipientAction]
}
I used one of the JSON macros included with Play that will automatically create a Writes for a case class, since all we care about is printing the list.
Then in your template:
#(actions : List[RecipientAction])
#import play.api.libs.json.Json
<script type="text/javascript">
var testArray = #Html(Json.stringify(Json.toJson(actions)));
</script>
The definition of an implicit Writes is required so that Json.toJson knows how to convert the class to JSON. For more about Json serialization/deserialization see the documentation.

supplying initialization json string as parameter to jquery method

i have just begun using jquery datatables in my project and I do like it so far. I have many tables, sometimes 2-3 on a page. Rather than have to keep track of what initialization string I am using for a specific table and trying to remember what webpage its on, I have built an xml file to store all the initialization strings. I built some jquery functions to retrieve the strings on document ready but it never dawned on me how to actually inject the json into the method as a parameter.
If i was doing it manually you would call
selector.dataTables(json initializer string here);
Once I have that string how do I actually inject it into the method call? Or do I have to create that whole code line and inject it into my script?
If the json data comes in as something like this:
{"order": [[ 3, "desc" ]]}
You could use jquery to get the JSON via a HTTP GET request.
$.getJSON('somejson.json',function(data){
someSelector.dataTables(data)
});
Because you are using getJSON it will expect the JSON to be in that format and do the parsing for you.
Or if the JSON is available already(since you are using jquery you can use it to parse the JSON data just in case there may be a browser support issue since IE7 and below does not support JSON.parse.):
var options = $.parseJSON(someData);
someSelector.dataTables(options)
you can assign the json string to a variable...
var tableSettings = theJsonString;
selector.dataTables(tableSettings);
you may need to convert the string to an object first...
//javascript
var tableSettings = JSON.parse(theJsonString);
//jquery
var tableSettings = $.parseJSON(theJsonString);

theymeleaf inline javascript framework issue

<script th:inline="javascript" type="text/javascript">
//expose list data to javascript
var listObject = /*[[${listObject}]]*/ [];
</script>
the replacement text printed into the file is different than what Jackson library's ObjectMapper does.
With Thymeleaf in above example, listObject will be
{
"dataType":{
"$type":"DataType",
"$name":"STRING"
},
"friendlyName":"Customer Key"
}
If I print the object with ObjectMapper(which is also used with Spring #RequestBody/#ResponseBody), it will be
{
"dataType":"STRING",
"friendlyName":"Customer Key"
}
Is there a way I can force thymeleaf to be compatible with ObjectMapper.
I think this has to say something about Jackson and JSON inlining in thymeleaf.
To summarize, the possibility to switch to custom TextInliners is considered for
3.0 thymeleaf milestone.
So, currently there is no "clean" way to switch to Jackson json serialization.
What you can do however, is sneak your own TextInliner. That is:
Create a class org.thymeleaf.standard.inliner.StandardJavaScriptTextInliner.
Implement your own version of formatEvaluationResult(Object) method,
where you can call the Jackson ObjectMapper .
Put this new StandardJavaScriptTextInliner class in a proper place, so that it is loaded before the original class (f.e. in tomcat put it in classes dir under correct package structure).
Another option:
when you set listObject in the thymeleaf context, set it to the string that is obtained by converting listObject to a JSON string using Jackson
then use JS eval() or the better method - JSON.parse to convert the string into a JS object.

Categories