I've been playing around with Spring MVC and came across an issue, where I send a JSON object from the frontend with JavaScript to the backend. To do so, I use a POST Method, which seems to return null values on the backend.
JavaScript:
function calcBudget() {
var table = document.getElementById("mainTable");
const jsonBody = [];
for (var i = 1; i < table.rows.length; i++) {
const jsonItem = {};
jsonItem ["name"] = table.rows[i].cells[0].firstChild.value;
jsonItem ["category"] = table.rows[i].cells[1].firstChild.value;
jsonItem ["amount"] = table.rows[i].cells[2].firstChild.value;
jsonBody.push(jsonItem);
}
console.log(JSON.stringify(jsonBody));
var xhr = new XMLHttpRequest();
xhr.open("POST", "/home/test", true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({
jsonBody
}));
}
JavaScript Output:
[{"name":"01","category":"01","amount":"03"}]
RestController:
#Controller
public class GreetingsController {
#PostMapping("/home/test")
public String readInput(#RequestBody CalcModel model) {
System.out.println(model.expenseName +" " + model.expenseCategory+" "+ model.expenseAmount);
return "index";
}
}
RestController Output:
null null null
CalcModel:
public class CalcModel {
public String expenseName;
public String expenseCategory;
public Double expenseAmount;
public CalcModel(String expenseName, String expenseCategory, Double expenseAmount) {
this.expenseName = expenseName;
this.expenseCategory = expenseCategory;
this.expenseAmount = expenseAmount;
}
}
Why is the RestController output null? I have attempted to integrate an H2 database, as I assumed I would need one. Turned out, though, that this does not work either. I also tried to use getters for the CalcModel, without any luck. Any ideas?
Edit:
It seems as I'm sending a JSON array to Spring Boot, which expects to find just a single object, not an array of objects. Removing the square brackets seems to solve the issue when testing via Postman. Now I need to find a solution how to make SpringBoot recognize the response body as a JSON array.
Solution:
Change RestController Method to this (Request Body needs to be an ArrayList)
#PostMapping("/home/test")
public String readInput(#RequestBody ArrayList<CalcModel> model) {
for (CalcModel calcModel : model) {
System.out.println(calcModel.name + " " + calcModel.category + " " + calcModel.amount);
}
return "index";
}
In the JavaScript file, remove the curly brackets
xhr.send(JSON.stringify(jsonBody));
It seems like you have different namings in the jsonBody and the CalcModel. Try changing the names to the same thing.
[{"name":"01","category":"01","amount":"03"}]
public class CalcModel {
public String name;
public String category;
public Double amount;
You might also need a parameterless constructor and getters and setters.
I'm using the jQuery UI autocomplete to show suggestions for a text input field.
The suggestions are stored in a Javascript array called suggestions.
I'm trying to fetch the string values for the suggestions array from a database, but I can't convert the List object to a Javascript array.
The Javascript:
var suggestions = [];
$.get('/mywebapp/autocompleteplayer.html', function(data){
parsed = JSON.parse(data);
suggestions = parsed.split(",");
}, "json");
$('#autocompleted').autocomplete({
data: suggestions,
minLength: 3
});
The Spring MVC controller:
#Controller
public class AutocompletePlayerController {
#RequestMapping(value = "/autocompleteplayer")
public List<String> getPlayerSuggestions(){
List<String> myList;
//code that fills myList with all of the players' full names from the database, omitted for brevity
return myList;
}
}
I know I'm not parsing the AJAX response properly, since I've checked from the browser console that the suggestions array has 0 elements. Can anyone help me? What am I doing wrong here?
You need to use it like this in your js (data is the response entity, not the data - it's in data.data) :
$.get('/mywebapp/autocompleteplayer', function(data){
suggestions = data.data;
$('#autocompleted').autocomplete({ // not sure how to use your plugin
data: suggestions,
minLength: 3
});
}, "json");
And in your spring controller, add the #ResponseBody Annotation
#RequestMapping(value = "/autocompleteplayer")
#ResponseBody
public List<String> getPlayerSuggestions(){
List<String> myList;
//code that fills myList with all of the players' full names from the database, omitted for brevity
return myList;
}
There's no need to JSON.parse anything, jquery already did this.
/mywebapp/autocompleteplayer.html also sounds wrong. The route is autocompleteplayer without .html.
Other than that, suggestions is created async. So your data in autocomplete will always be empty. I didn't use that plugin myself so there might be an update function for the data, otherwise try to put it in the $.get() functions callback.
You can return json array as String in java
#Produces("application/json")
public String getPlayerSuggestions(){
....
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String jsonString = gson.toJson(myList);
return jsonString;
}
You need to modify your controller like this:
#RequestMapping(value = "/autocompleteplayer", method=RequestMethod.GET)
#ResponseBody
public String getPlayerSuggestions(){
List<String> myList;
JSONArray jsonArray = new JSONArray();
for(String item: myList){
JSONObject jsonObj = new JSONObject();
jsonObj.put("Item", item);
jsonArray.put(jsonObj);
}
return jsonArray.toString();
}
then you can handle your values easy with JSON.
Regards,
I just used the XmlWriter to create some XML to send back in an HTTP response. How would you create a JSON string. I assume you would just use a stringbuilder to build the JSON string and them format your response as JSON?
Using Newtonsoft.Json makes it really easier:
Product product = new Product();
product.Name = "Apple";
product.Expiry = new DateTime(2008, 12, 28);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };
string json = JsonConvert.SerializeObject(product);
Documentation: Serializing and Deserializing JSON
You could use the JavaScriptSerializer class, check this article to build an useful extension method.
Code from article:
namespace ExtensionMethods
{
public static class JSONHelper
{
public static string ToJSON(this object obj)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(obj);
}
public static string ToJSON(this object obj, int recursionDepth)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RecursionLimit = recursionDepth;
return serializer.Serialize(obj);
}
}
}
Usage:
using ExtensionMethods;
...
List<Person> people = new List<Person>{
new Person{ID = 1, FirstName = "Scott", LastName = "Gurthie"},
new Person{ID = 2, FirstName = "Bill", LastName = "Gates"}
};
string jsonString = people.ToJSON();
Simlpe use of Newtonsoft.Json and Newtonsoft.Json.Linq libraries.
//Create my object
var myData = new
{
Host = #"sftp.myhost.gr",
UserName = "my_username",
Password = "my_password",
SourceDir = "/export/zip/mypath/",
FileName = "my_file.zip"
};
//Tranform it to Json object
string jsonData = JsonConvert.SerializeObject(myData);
//Print the Json object
Console.WriteLine(jsonData);
//Parse the json object
JObject jsonObject = JObject.Parse(jsonData);
//Print the parsed Json object
Console.WriteLine((string)jsonObject["Host"]);
Console.WriteLine((string)jsonObject["UserName"]);
Console.WriteLine((string)jsonObject["Password"]);
Console.WriteLine((string)jsonObject["SourceDir"]);
Console.WriteLine((string)jsonObject["FileName"]);
This library is very good for JSON from C#
http://james.newtonking.com/pages/json-net.aspx
This code snippet uses the DataContractJsonSerializer from System.Runtime.Serialization.Json in .NET 3.5.
public static string ToJson<T>(/* this */ T value, Encoding encoding)
{
var serializer = new DataContractJsonSerializer(typeof(T));
using (var stream = new MemoryStream())
{
using (var writer = JsonReaderWriterFactory.CreateJsonWriter(stream, encoding))
{
serializer.WriteObject(writer, value);
}
return encoding.GetString(stream.ToArray());
}
}
If you need complex result (embedded) create your own structure:
class templateRequest
{
public String[] registration_ids;
public Data data;
public class Data
{
public String message;
public String tickerText;
public String contentTitle;
public Data(String message, String tickerText, string contentTitle)
{
this.message = message;
this.tickerText = tickerText;
this.contentTitle = contentTitle;
}
};
}
and then you can obtain JSON string with calling
List<String> ids = new List<string>() { "id1", "id2" };
templateRequest request = new templeteRequest();
request.registration_ids = ids.ToArray();
request.data = new templateRequest.Data("Your message", "Your ticker", "Your content");
string json = new JavaScriptSerializer().Serialize(request);
The result will be like this:
json = "{\"registration_ids\":[\"id1\",\"id2\"],\"data\":{\"message\":\"Your message\",\"tickerText\":\"Your ticket\",\"contentTitle\":\"Your content\"}}"
Hope it helps!
You can also try my ServiceStack JsonSerializer it's the fastest .NET JSON serializer at the moment. It supports serializing DataContracts, any POCO Type, Interfaces, Late-bound objects including anonymous types, etc.
Basic Example
var customer = new Customer { Name="Joe Bloggs", Age=31 };
var json = JsonSerializer.SerializeToString(customer);
var fromJson = JsonSerializer.DeserializeFromString<Customer>(json);
Note: Only use Microsofts JavaScriptSerializer if performance is not important to you as I've had to leave it out of my benchmarks since its up to 40x-100x slower than the other JSON serializers.
Take a look at http://www.codeplex.com/json/ for the json-net.aspx project. Why re-invent the wheel?
If you want to avoid creating a class and create JSON then Create a dynamic Object and Serialize Object.
dynamic data = new ExpandoObject();
data.name = "kushal";
data.isActive = true;
// convert to JSON
string json = Newtonsoft.Json.JsonConvert.SerializeObject(data);
Read the JSON and deserialize like this:
// convert back to Object
dynamic output = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
// read a particular value:
output.name.Value
ExpandoObject is from System.Dynamic namespace.
If you can't or don't want to use the two built-in JSON serializers (JavaScriptSerializer and DataContractJsonSerializer) you can try the JsonExSerializer library - I use it in a number of projects and works quite well.
If you're trying to create a web service to serve data over JSON to a web page, consider using the ASP.NET Ajax toolkit:
http://www.asp.net/learn/ajax/tutorial-05-cs.aspx
It will automatically convert your objects served over a webservice to json, and create the proxy class that you can use to connect to it.
Encode Usage
Simple object to JSON Array EncodeJsObjectArray()
public class dummyObject
{
public string fake { get; set; }
public int id { get; set; }
public dummyObject()
{
fake = "dummy";
id = 5;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append('[');
sb.Append(id);
sb.Append(',');
sb.Append(JSONEncoders.EncodeJsString(fake));
sb.Append(']');
return sb.ToString();
}
}
dummyObject[] dummys = new dummyObject[2];
dummys[0] = new dummyObject();
dummys[1] = new dummyObject();
dummys[0].fake = "mike";
dummys[0].id = 29;
string result = JSONEncoders.EncodeJsObjectArray(dummys);
Result:
[[29,"mike"],[5,"dummy"]]
Pretty Usage
Pretty print JSON Array PrettyPrintJson() string extension method
string input = "[14,4,[14,\"data\"],[[5,\"10.186.122.15\"],[6,\"10.186.122.16\"]]]";
string result = input.PrettyPrintJson();
Results is:
[
14,
4,
[
14,
"data"
],
[
[
5,
"10.186.122.15"
],
[
6,
"10.186.122.16"
]
]
]
The DataContractJSONSerializer will do everything for you with the same easy as the XMLSerializer. Its trivial to use this in a web app. If you are using WCF, you can specify its use with an attribute. The DataContractSerializer family is also very fast.
I've found that you don't need the serializer at all. If you return the object as a List.
Let me use an example.
In our asmx we get the data using the variable we passed along
// return data
[WebMethod(CacheDuration = 180)]
public List<latlon> GetData(int id)
{
var data = from p in db.property
where p.id == id
select new latlon
{
lat = p.lat,
lon = p.lon
};
return data.ToList();
}
public class latlon
{
public string lat { get; set; }
public string lon { get; set; }
}
Then using jquery we access the service, passing along that variable.
// get latlon
function getlatlon(propertyid) {
var mydata;
$.ajax({
url: "getData.asmx/GetLatLon",
type: "POST",
data: "{'id': '" + propertyid + "'}",
async: false,
contentType: "application/json;",
dataType: "json",
success: function (data, textStatus, jqXHR) { //
mydata = data;
},
error: function (xmlHttpRequest, textStatus, errorThrown) {
console.log(xmlHttpRequest.responseText);
console.log(textStatus);
console.log(errorThrown);
}
});
return mydata;
}
// call the function with your data
latlondata = getlatlon(id);
And we get our response.
{"d":[{"__type":"MapData+latlon","lat":"40.7031420","lon":"-80.6047970}]}
Include:
using System.Text.Json;
Then serialize your object_to_serialize like this:
JsonSerializer.Serialize(object_to_serialize)
I have an array in javascript that I am trying to pass to my mobilefirst java adapter. I call my adapter like so,
myArr = [1,2,3];
var sendPost = new WLResourceRequest(
"/adapters/MyAdpater/path",
WLResourceRequest.POST
);
var formParam = {"arr":myArr};
sendTicketPost.sendFormParameters(formParams);
Then in my adapter I can have my method and get the param
public JSONObject postAdapterFx(#FormParam("arr") List<Integer> myArray) {}
Currently when I send this I just get a 400 error and it is because the adapter doesnt like the form param as that type, so what else could I set myArray to in the adapter? I can send it as a string and then convert the string to a List<Integer> in the java but that is really messy and I would like to avoid doing that.
So how can I pass this array?
Thanks for the help
you can do it in body with request.send(yourArray). Then you can read it in Java with buffer.
Example from knowledge center
var request = WLResourceRequest(url, method, timeout);
request.send(json).then(
function(response) {
// success flow
},
function(error) {
// fail flow
}
);
You will need to take an extra step before sending the form data to the adapter. sendFormParameters only accepts objects with simple values i.e., string, integer, and boolean; in your case arr is an array.
Create a utility function that will encode the form data, you can use the following:
function encodeFormData(data) {
var encoded = '';
for(var key in data) {
var value = data[key];
if(value instanceof Array) {
encoded += encodeURI(key+'='+value.join('&'+key+'='));
} else {
encoded += encodeURI(key+'='+value);
}
encoded += '&';
}
return encoded.slice(0, -1);
}
Then you will need to update your code as follows:
var myArr = [9,2,3];
var sendPost = new WLResourceRequest("/adapters/Cool/users/cool", WLResourceRequest.POST);
var formParam = {"arr" : myArr};
sendPost.sendFormParameters(encodeFormData(formParam));
I am calling a WCF method from an ASP.Net page, but I am getting format exception when WCF tries to deserialize the recordIds parameter received from JavaScript.
The first parameter passed to the WCF method needs to be of List type. Is there something wrong I have done in using JSON.stringify?
Javascript Code to call WCF
function Update() {
var myarray1 = new Array();
myarray1[0] = 1;
myarray1[1] = 11;
myarray1[2] = 14;
WCFService1.AJAXEnabledService.BatchUpdateRecords(
JSON.stringify({recordIDs: myarray1}) , "ddsd", "gggg",
updateGrid, OnError);
}
WCF method being called by above JavaScript
[OperationContract]
public bool BatchUpdateRecords(List<int> recordIds, string columnNameToUpdate, string columnValue)
{
DataTable tierIDsTable = new DataTable("RecordIds");
tierIDsTable.Columns.Add(new DataColumn("Integer", typeof(Int32)));
tierIDsTable.PrimaryKey = new DataColumn[] { tierIDsTable.Columns["TierId"] };
foreach (int recordId in recordIds)
{
tierIDsTable.Rows.Add(recordId);
}
return true;
}
Not 100% sure, but have you tried this?
WCFService1.AJAXEnabledService.BatchUpdateRecords(
myarray1,
"ddsd",
"gggg",
updateGrid, OnError);
The issue (without knowing the error that you are receiving) is most likely that you are trying to pass in multiple parameters types. WCF does not usually support and expects an object instead. Create a response class with your parameters and use that instead.
public class ResponseObject
{
public List<int> recordIds { get; set; }
public string columnNameToUpdate { get; set; }
public string columnValue { get; set; }
}
Use this object as your parameter
public bool BatchUpdateRecords(ResponseObject responseObject)
{...