Passing Javascript object throught JavascriptInterface to Android application [duplicate] - javascript

Is it possible to pass a JavaScript object from JavaScript to Java using addJavascriptInterface()? Something along these lines:
var javaScriptObject = {"field1":"string1", "field2":"string2"};
JavaScriptInterface.passObject(javaScriptObject);
How would such a call be captured on the Java side? I have no problem setting up the interface to send a string, but when I send an object, I receive null on the Java end.

AFAIK, addJavascriptInterface() only works with primitive types and Strings, and so you cannot pass arbitrary Javascript objects.

This is how I am doing...
In Android...
#JavascriptInterface
public void getJSONTData(String jsonData) {
try {
JSONObject data = new JSONObject(jsonData); //Convert from string to object, can also use JSONArray
} catch (Exception ex) {}
}
In JavaScript...
var obj = { Name : 'Tejasvi', Age: 100};
var str = JSON.stringify(obj);
Android.getJSONTData(str);
As of now, I could not find any other proper way to pass the native JavaScript object directly to JavascriptInterface.
Calling Android.getJSONTData({ Name : 'Tejasvi', Age: 100}) results in null (if parameter type is Object) or undefined (if parameter type is defined as String) in getJSONTData.

I found a solution, using JSON. My Java method returns a JSONArray, on my javascript code I receive this and convert to a javascript vector using JSON.parse(). See the example:
Java:
public class JavaScriptInterface {
Context mContext;
private static int ind=-1;
private static int [] val = { 25, 25, 50, 30, 40, 30, 30, 5, 9 };
public JavaScriptInterface(Context c) {
mContext = c;
}
#JavascriptInterface
public JSONArray getChartData() {
String texto = " [ {name: 'valor1', 2007: "+val[(++ind)%9]+"}, "+
" {name: 'valor2', 2007: "+val[(++ind)%9]+"}, "+
" {name: 'valor3', 2007: "+val[(++ind)%9]+"} ]";
JSONArray jsonar=null;
try {
jsonar = new JSONArray(texto);
} catch (JSONException e) {
e.printStackTrace();
}
return jsonar;
}
}
Now the javascript code:
window.generateData = function() {
/*var data = [ {name: 'valor1', 2007: 50},
{name: 'valor2', 2007: 20},
{name: 'valor3', 2007: 30} ]; */
var data = JSON.parse( Android.getChartData() );
return data;
};
The commented code above show how it was when static, and now the data came from the Java code.
It was testes on Android 2.1 and 3.2.

I can run this feature
In Javascript :
var data = {
'username' : $('#username').val().trim(),
'password' : $('#password').val().trim(),
'dns' : $('#dns').val().trim()
}
var str = JSON.stringify(data);
Native.getLoginService(str);
In Android :
#JavascriptInterface
public void getLoginService(String jsonData){
try{
JSONObject data = new JSONObject(jsonData);
String username = data.getString("username");
String password = data.getString("password");
String dns = data.getString("dns");
Log.i("TAG",username + " - " + password + " - " + dns);
}catch (Exception ex){
Log.i("TAG","error : " + ex);
}
}
Good luck with...

I think you can also pass JSONObject and JSONArray. So not only primitive types, but also primitive types stored in a javascript array [0,1,2] or dictionary {one:1, two:2}.
I have NOT verified this in code, just read the docs. Might be using it soon.

You can't pass JSONObject or JSONArray, but you can send strings with that form and parse them to those types.
Your option is to expose the method using strings and then you can use the JSONObject or JSONArray to parse the string and use it accordingly.
Here is what I did.
#JavascriptInterface
public void passJSON(String array, String jsonObj) throws JSONException
{
JSONArray myArray = new JSONArray(array);
JSONObject myObj = new JSONObject(jsonObj);
...
}
where array is '["string1","string2"]' and jsonObj is '{attr:1, attr2:"myName"}'

Related

JSONObject can't read attribute from existing object even though object is correctly instanced

I have problem with reading json attribute even though object correctly instanced.
First, I send json from client side with JavaScript:
let object = {
firstName: document.getElementById("firstName").value,
lastName: document.getElementById("lastName").value,
username: document.getElementById("username").value,
password: document.getElementById("password").value,
email: document.getElementById("email").value,
action: "registration"
}
let request = new XMLHttpRequest();
...
On server side I have code:
req.setCharacterEncoding("UTF-8");
JSONObject jsonObject = null;
// String address = "/WEB-INF/pages/login.jsp";
StringBuffer jb = new StringBuffer();
String line = null;
try {
BufferedReader reader = req.getReader();
while ((line = reader.readLine()) != null)
jb.append(line);
} catch (Exception e) {
/* report an error */ }
try {
jsonObject = HTTP.toJSONObject(jb.toString());
} catch (JSONException e) {
// crash and burn
throw new IOException("Error parsing JSON request string");
}
String action = jsonObject.getString("firstName");
jsonObject exists but program throws org.json.JSONException: JSONObject["firstName"] not found.
Object on server side when I use debugger:
There is no key with a name like firstName in your jsonObject. Instead, you need to search for Method property and then parse the firstName from it. First, declare a GetQueryMap method:
public static Map<String, String> GetQueryMap(String query)
{
String[] params = query.split("&");
Map<String, String> map = new HashMap<String, String>();
for (String param : params)
{
String [] p=param.split("=");
String name = p[0];
if(p.length>1) {
String value = p[1];
map.put(name, value);
}
}
return map;
}
Then use it like:
String method = jsonObject.getString("Method");
Map params = GetQueryMap(method);
String firstName = (String)params.get("firstName");
String lastName = (String)params.get("lastName");
I think the issue is that you're not sending the data correctly from the browser side.
are you sending the correct content-type header (application/json)?
Are you serializing the object correctly for sending?

Error deserializing JSON with Newtonsoft, C#

I'm receiving the following error:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'System.Collections.Generic.Dictionary`2[System.String,System.Collections.Generic.List`1[ReportingDataSchema.CurrentBusinessUnits]]'
because the type requires a JSON object (e.g. {\"name\":\"value\"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {\"name\":\"value\"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array.
JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array. Path 'selectedBusinessUnits', line 1, position 26."} System.Exception {Newtonsoft.Json.JsonSerializationException}
My JSON:
{
"selectedBusinessUnits": [{
"guidNode": "some value",
"businessUnit": "some value",
"fileName": "some value"
}, {
...
}]
}
I'm trying to first transform this response into the following:
public class EnAFileGenerator
{
private Dictionary<string, List<CurrentBusinessUnits>> selectedBusinessUnits;
public Dictionary<string, List<CurrentBusinessUnits>> SelectedBusinessUnits
{
get { return selectedBusinessUnits; }
set { selectedBusinessUnits = value; }
}
}
So that ultimately I can access the array in the JSON, using the following:
public class CurrentBusinessUnits
{
private string guidNode;
private string businessUnit;
private string fileName;
public string GuidNode { get; set; }
public string BusinessUnit { get; set; }
public string FileName { get; set; }
}
The code that's generating the error:
// JSON Data is the parameter from the client, containing the above JSON object
EnAFileGenerator resultArray = JsonConvert.DeserializeObject<EnAFileGenerator>(JSONData);
From what I've read, it seems like my error is a result of the parsing the array (the value for the property selectedBusinessUnits) into the desired C# collection.
After implementing #DavidG's suggestion, I'm still receiving the following:
Error converting value \"{\"guidNode\":\"some value\",\"businessUnit\":\"some value\",\"fileName\":\"some value.xlsx\"}\"
to type 'ReportingDataSchema.CurrentBusinessUnits'. Path 'selectedBusinessUnits[0]', line 1, position 159."}
System.Exception {Newtonsoft.Json.JsonSerializationException}
Prany's solution nearly got me there. I was able to modify that code to utilize the objects I already had:
var files = JObject.Parse(JSONData);
var recList = files.SelectToken("$..selectedBusinessUnits").ToList();
foreach (string item in recList)
{
JObject businessUnit = JObject.Parse(item);
CurrentBusinessUnits currentBusinessUnit = businessUnit.ToObject<CurrentBusinessUnits>();
}
The problem is you are trying to deserialise into the wrong type. you have specified a Dictionary<string, List<CurrentBusinessUnits>> but really you only need a List<CurrentBusinessUnits>:
public class EnAFileGenerator
{
public List<CurrentBusinessUnits> SelectedBusinessUnits { get; set; }
}
You can use Jobject since you're using Newtonsoft. For getting values based on selectedBusinessUnits. Use below
var files = JObject.Parse(YourJson);
var recList = files.SelectToken("$..selectedBusinessUnits").ToList();
foreach (JObject item in recList)
{
foreach (JProperty prop in item.Children())
{
string key = prop.Name.ToString();
string value = prop.Value.ToString();
}
}

Getting the value of JavaScript/HTML variables in C#

There is a webpage I am trying to extract data from. By looking at the HTML in the page Source, I can find the data I am interested inside script tags.
It looks like the following:
<html>
<script type="text/javascript">
window.gon = {};
gon.default_profile_mode = false;
gon.user = null;
gon.product = "shoes";
gon.books_jsonarray = [
{
"title": "Little Sun",
"authors": [
"John Smith"
],
edition: 2,
year: 2009
},
{
"title": "Little Prairie",
"authors": [
"John Smith"
],
edition: 3,
year: 2009
},
{
"title": "Little World",
"authors": [
"John Smith",
"Mary Neil",
"Carla Brummer"
],
edition: 3,
year: 2014
}
];
</script>
</html>
What I would like to achieve is, call the webpage by using its url, then retrieving the 'gon' variable from JavaScript and store it in a C# variable. In other words, in C#, I would like to have a data structure (a dictionary for instance) that would hold the value of 'gon'.
I have tried researching how to get a variable defined in JavaScript via C# WebBrowser, and this is what I found:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Net;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using mshtml;
namespace Mynamespace
{
public partial class Form1 : Form
{
public WebBrowser WebBrowser1 = new WebBrowser();
private void Form1_Load(object sender, EventArgs e)
{
string myurl = "http://somewebsite.com"; //Using WebBrowser control to load web page
this.WebBrowser1.Navigate(myurl);
}
private void btnGetValueFromJs_Click(object sender, EventArgs e)
{
var mydoc = this.WebBrowser1.Document;
IHTMLDocument2 vDocument = mydoc.DomDocument as IHTMLDocument2;
IHTMLWindow2 vWindow = (IHTMLWindow2)vDocument.parentWindow;
Type vWindowType = vWindow.GetType();
object strfromJS = vWindowType.InvokeMember("mystr",
BindingFlags.GetProperty, null, vWindow, new object[] { });
//Here, I am able to see the string "Hello Sir"
object gonfromJS = vWindowType.InvokeMember("gon",
BindingFlags.GetProperty, null, vWindow, new object[] { });
//Here, I am able to see the object gonfromJS as a '{System.__ComObject}'
object gonbooksfromJS = vWindowType.InvokeMember("gon.books_jsonarray",
BindingFlags.GetProperty, null, vWindow, new object[] { });
//This error is thrown: 'An unhandled exception of type 'System.Runtime.InteropServices.COMException' occurred in mscorlib.dll; (Exception from HRESULT: 0x80020006 (DISP_E_UNKNOWNNAME))'
}
}
}
I am able to retrieve values of string or number variables such as:
var mystr = "Hello Sir";
var mynbr = 8;
However, even though I am able to see that the 'gon' variable is being passed as a '{System.__ComObject}', I don't know how to parse it in order to see the values of its sub components. It would be nice if I could parse it, but if not, what I would like to have instead, is a C# Data Structure with keys/values that contains all the sub infos for the gon variable, and especially, be able to view the variable 'gon.books_jsonarray'.
Any help on how to achieve this would be very much appreciated. Note that I cannot change the source html/javascript in anyway, and so, what I need is a C# code that would allow to reach my goal.
You can cast the result of InvokeMember() to dynamic and use the property names directly in your C# code. Array indexing is tricky but can be done with another use of InvokeScript(), see my example:
private void btnGetValueFromJs_Click(object sender, EventArgs e)
{
var mydoc = this.WebBrowser1.Document;
IHTMLDocument2 vDocument = mydoc.DomDocument as IHTMLDocument2;
IHTMLWindow2 vWindow = (IHTMLWindow2)vDocument.parentWindow;
Type vWindowType = vWindow.GetType();
var gonfromJS = (dynamic)vWindowType.InvokeMember("gon",
BindingFlags.GetProperty, null, vWindow, new object[] { });
var length = gonfromJS.books_jsonarray.length;
for (var i = 0; i < length; ++i)
{
var book = (dynamic) mydoc.InvokeScript("eval", new object[] { "gon.books_jsonarray[" + i + "]" });
Console.WriteLine(book.title);
/* prints:
* Little Sun
* Little Prairie
* Little World
*/
}
}
You need to use JSON.stringify to convert your gon.books_jsonarray variable to JSON string
After you can retrive JSON using next C# code:
var gonFromJS = mydoc.InvokeScript("eval", new object[] { "JSON.stringify(gon.books_jsonarray)" }).ToString();
After you can deserialize JSON to object using Newtonsoft.Json
My full code is here:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var webBrowser = new WebBrowser();
webBrowser.DocumentCompleted += (s, ea) =>
{
var mydoc = webBrowser.Document;
var gonFromJS = mydoc.InvokeScript("eval", new object[] { "JSON.stringify(gon.books_jsonarray)" }).ToString();
var gonObject = JsonConvert.DeserializeObject<List<Books>>(gonFromJS);
};
var myurl = "http://localhost/test.html";
webBrowser.Navigate(myurl);
}
private class Books
{
public string Title { get; set; }
public List<string> Authors { get; set; }
public int Edition { get; set; }
public int Year { get; set; }
}
}
}
Also you can see output on screenshot:
EDIT:
Also you can have a trouble with JSON.stringify method.
It can returns null.
In this case you can review SO topics: here and here.
If JSON.stringify method returns null then try to add next code to your HTML page:
<head>
<meta http-equiv='X-UA-Compatible' content='IE=edge' >
</head>

ASP.NET MVC - How to "reverse" model binding to convert a C# model back to a query string representation

I have a custom javascript on the client side that I use to build up a querystring and pass over to my asp.net-mvc controller
var templateQueryString = BuildTemplate();
$.ajax({
url: '/MyController/Save?' + templateQueryString,
type: 'post',
dataType: 'json',
success: function (data) {
}
}
and on my controller all of the properties leverage the model binding so it comes in as a single object on the server side. NOTE: that this is a pretty complex object with arrays and arrays of sub objects:
public ActionResult Save(MyTemplate template)
{
}
the issue now is that I need to be able to convert from my C# object back to a string that represents "myTemplateQueryString" on the client side.
Is there any recommended way to take an object and do the "reverse" model binding. They key here is that it generates a string that I could use as a query string again in the future to pass into another asp.ent-mvc controller action.
Here is an example of the querystring that I am storing locally:
<input type="hidden" value="showIds=false&showRisks=false&
amp;statusIds=2&statusIds=1&statusIds=6&statusIds=8&
amp;statusIds=3&statusIds=9&showCompleted=0"
name="filterQueryString" id="filterQueryString">
As #haim770 said it would be easier if you used JSON in the request payload, and not the query string to pass your complex object to the server.
Regarding creating the query string from a model there is not a built-in method that does something like that or any recommended approach as far as i know. An obvious solution is to use reflection and build the query string from your properties.
Assuming your BuildTemplate class looks something like:
public class BuildTemplate
{
public bool ShowIds { get; set; }
public bool ShowRisks { get; set; }
public bool ShowCompleted { get; set; }
public int[] StatusIds { get; set; }
}
You can develop an extension method to convert any object to a QueryString. Here is some initial code you can start with:
public static class ObjectExtensions
{
public static string ToQueryString(this Object obj)
{
var keyPairs = obj.GetType().GetProperties().Select(p =>
new KeyValuePair<string, object>(p.Name.ToLower(), p.GetValue(obj, null)));
var arr = new List<string>();
foreach (var item in keyPairs)
{
if (item.Value is IEnumerable && !(item.Value is String))
{
foreach (var arrayItem in (item.Value as IEnumerable))
{
arr.Add(String.Format("{0}={1}", item.Key, arrayItem.ToString().ToLower()));
}
}
else
arr.Add(String.Format("{0}={1}", item.Key, item.Value.ToString().ToLower()));
}
return "?" + String.Join("&", arr);
}
}
Then you can easily invoke this code on any object to generate a query string:
var person = new BuildTemplate() { StatusIds = new []{ 1, 5, 8, 9 }, ShowRisks = true };
var queryString = person.ToQueryString();
This would generate a query string like:
"?showids=false&showrisks=true&showcompleted=false&statusids=1&statusids=5&statusids=8&statusids=9"
This query string should work just fine with the default model binder for the BuildTemplate class.

Android Phonegap - Passing JSONObject to javascript

I'm trying to pass some information from an Android native class to the javascript.
I'm taking a bundle, converts it to JSONObject and passing the string representation of it.
But when trying to parse it in the JS, it fails.
This is what I do:
JSONObject jsonObject = new JSONObject();
for (String key : bundle.keySet()) {
Object value = bundle.get(key);
try {
jsonObject.put(key, value.toString());
} catch (JSONException e) {
// Do nothing
}
}
final String jsStatement = String.format(
"window.doSomething('%s');", jsonObject.toString());
cordova.getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
webView.loadUrl("javascript:" + jsStatement);
}
});
Can you tell me why it's not being parsed in the JS and how can I solve it?
Look into the Javascript Interface annotation which can be used to pass in / access the values from Java directly

Categories

Resources