I am trying to parse a JSON object passed from a WCF Service, here is the Response the service gives
[
{
"Caption": "Hello",
"CityState": "Seattle",
"URL": "Test"
},
{
"Caption": "World",
"CityState": "Chicago",
"URL": "Test"
},
{
"Caption": "Hello",
"CityState": "New York",
"URL": "Test"
}
]
Here is the WCF Code to create the object
///Custom Object has 3 string properties
List<OW_CompanyACSearch> search = new List<OW_CompanyACSearch>();
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(List<OW_CompanyACSearch>));
using (MemoryStream stream = new MemoryStream())
{
serializer.WriteObject(stream, search);
return Encoding.Default.GetString(stream.ToArray());
}
and here is the JQuery that I am trying to make work
source: function (request, response) {
j$.ajax({
url:"testservice.svc",
dataType: "json",
data: {
Search: request.term
},
success: function (data) {
response(j$.map(data, function (item) {
return {
label: item.Caption,
value: item.Caption,
URL: item.URL,
CityState: item.CityState
}
}));
}
});
}
The issue that I am having is that when I fall into the return to parse the members on the object, none of the properties are defined. If I drill down into the object, it seems that it is treating object as one long string, so index[0] would be "[" and so on.
I have read all over and I have tried every option that I have seen and I still get this issue. I am not sure if I am serializing or parsing incorrectly, but any help would be appreciated.
Also, not sure if it matters, but the binding on the WCF is webHttpBinding
Wow this was awesome, come to find out that data was just a wrapper for the JSON object, and there was a property named "d" that was the actual object.
So this
data = j$.parseJSON(data.d);
filled data with the object, and I could move forward.
The data object should already be a variable array that contains your data at the properties as named by the JSON. You should be able to do something like this:
source: function (request, response) {
j$.ajax({
url:"testservice.svc",
dataType: "json",
data: {
Search: request.term
},
success: function (data) {
alert(data[0].Caption);
}
});
}
To verify the object structure.
Related
What I'm trying to do:
Create a chart and have it display data from the server (held in a C# object) every x mminutes. From googling using JSON (which I've never used before) would be best practice.
What I have so far:
I have the backend C# (using MVC 5) getting the correct data, lots of objects with lots of properties, some I want to display in the chart, others I don't.
I'v also started on a JSON function in my Index.cshtml which is where my graph is (currently set with static data, it's a simple jQuery plug-in).
The problem:
Unsure how to get specific object properties, to the JSON data and then to the chart data.
What would be the best way of achieving this?
Code:
Controller:
// GET: Graphs
public ActionResult Index()
{
return View();
}
public static List<server> GetServer()
{
Api api = new Api();
List<server> sList = api.GetServerStats();
return sList;
}
Script in INdex:
<script src="~/Scripts/canvasjs.min.js"></script>
<script type="text/javascript">
window.onload = function () {
var chart = new CanvasJS.Chart("someChart", {
title: {
text: "Space left on Server vs Total"
},
data: [
{
type: "column",
name: "Totals",
showInLegend: true,
dataPoints: [
{ label: "Space", y: 20 }
]
},
{
type: "column",
name: "Used",
showInLegend: true,
dataPoints: [
{ label: "Space", y: 10 }
]
}
],
axisY: {
prefix: "",
suffix: "GB"
}
});
chart.render();
}
$(document).ready(function () {
window.onload(function () {
$.ajax({
type: "POST",
url: "GraphController/GetServer",
data: { someParameter: "some value" },// array of values from object or just object
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (result) {
// need to push into jQuery function
}
});
});
});
</script>
Returning a serialised list of objects couldn't be easier with MVC, it takes a lot of the heavy lifting out of it for you if you just return a JsonResult:
public ActionResult GetServer() {
var api = new Api();
var = api.GetServerStats();
return Json(sList);
}
Debug the success of your ajax call to find out if result is what you want and expect, and then pass it to your chart script.
NOTE: From your use of wording (i.e. 'lots'), I'd recommend cutting down your list of server properties you return to your view. This is a perfect candidate for creating what's called a view model. This view model would be a kind of 'lite' version of your full server object. It would improve efficiency of serialisation for starters and, semantically, makes more sense.
I was doing same thing with highcharts and here is an approximate solution for you:
public ActionResult GetServer()
{
Dictionnary<server> sList = new Dictionnary<string,object>();
sList.Add("Totals",new{
type= "column",
name= "Totals",
showInLegend= true,
dataPoints= new List<dynamic>(){
new{ label= "Space", y= 20}/*,
new{ label= "label2", y= 30},*/
}
});
sList.Add("Totals",new{
type= "column",
name= "Used",
showInLegend= true,
dataPoints= new List<dynamic>(){
new{ label= "Space", y= 10}/*,
new{ label= "label2", y= 40},*/
}
});
return Json(sList, "text/json", JsonRequestBehavior.AllowGet);
}
Change window.onload=function(){} in
function drawChart(serverData){
var chart = new CanvasJS.Chart("someChart", {
title: {
text: "Space left on Server vs Total"
},
data: serverData,
axisY: {
prefix: "",
suffix: "GB"
}
});
chart.render();
}
$(document).ready(function () {
$.ajax({
type: "POST",
url: "GraphController/GetStuff",
data: { someParameter: "some value" },
// array of values from object or just object
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (serverData) {
// need to push into jQuery function
drawChart(serverData);
}
});
});
I have a Kendo UI Grid wired up to odata CRUD services (Web API 2.2 OData V4). The dataSource configuration looks like the following - the baseUrl is the same for all, just the HTTP verb changes.
var dataSource = new kendo.data.DataSource({
type: "odata",
transport: {
read: {
beforeSend: prepareRequest,
url: baseUrl,
type: "GET",
dataType: "json"
},
update: {
beforeSend: prepareRequest,
url: function (data) {
return baseUrl + "(" + data.CategoryId + ")";
},
type: "PUT",
dataType: "json"
},
create: {
beforeSend: prepareRequest,
url: baseUrl,
type: "POST",
dataType: "json"
},
destroy: {
beforeSend: prepareRequest,
url: function (data) {
return baseUrl + "(" + data.CategoryId + ")";
},
type: "DELETE",
dataType: "json"
},
parameterMap: function (data, operation) {
if (operation == "read") {
var paramMap = kendo.data.transports.odata.parameterMap(data);
delete paramMap.$format;
delete paramMap.$inlinecount;
paramMap.$count = true;
return paramMap;
} else if (operation == "create" || operation == "update") {
delete data["__metadata"];
return JSON.stringify(data);
}
}
},
batch: false,
pageSize: 10,
serverPaging: true,
serverSorting: true,
serverFiltering: true,
sort: { field: "CategoryCode", dir: "asc" },
schema: {
data: function (data) { return data.value; },
total: function (data) { return data['##odata.count']; },
model: {
id: "CategoryId",
fields: {
CategoryId: { editable: false, type: "number" },
CategoryCode: { editable: true, type: "string", required: true, validation: { maxlength: 2 } },
Description: { editable: true, type: "string", required: true, validation: { maxlength: 50 } },
Created: { editable: false, type: "date" },
CreatedBy: { editable: false, type: "string" },
Updated: { editable: false, type: "date" },
UpdatedBy: { editable: false, type: "string" }
}
}
},
error: function (e) {
commonNotification.hide();
commonNotification.show(getRequestError(e), "error");
},
change: function (e) {
commonNotification.hide();
if (e.action == "sync") {
commonNotification.show("#SharedResources.Changes_Saved", "success");
}
},
requestStart: function (e) {
if (e.type == "read" && this.hasChanges()) {
if (confirm("#SharedResources.Dirty_Navigation_Confirmation") == false) {
e.preventDefault();
} else {
this.cancelChanges();
}
}
}
});
Generally speaking, everything works great. The prepareRequest() function is used to apply a custom authorization header as well as setting the Accept header to "application/json;odata=verbose".
When reading, the JSON response looks something like the following - this is why the dataSource.schema.data function returns data.value
{
"#odata.context":"https://localhost:44305/odata/$metadata#Categories",
"#odata.count":2,
"value":[
{
"CategoryId":1,
"CategoryCode":"01",
"Description":"Something",
"Created":"2014-08-01T11:03:30.207Z",
"CreatedBy":"DOMAIN\\User",
"Updated":"2014-09-05T14:36:22.6323744-06:00",
"UpdatedBy":"DOMAIN\\User"
},{
"CategoryId":2,
"CategoryCode":"02",
"Description":"Something Else",
"Created":"2014-08-01T11:03:35.61Z",
"CreatedBy":"DOMAIN\\User",
"Updated":"2014-08-26T16:07:29.198241-06:00",
"UpdatedBy":"DOMAIN\\User"
}
]
}
However, when I create or update an entity, the JSON returned looks like this:
{
"#odata.context":"https://localhost:44305/odata/$metadata#Categories/$entity",
"CategoryId":3,
"CategoryCode":"03",
"Description":"Yet Another",
"Created":"2014-09-06T07:55:52.4933275-06:00",
"CreatedBy":"DOMAIN\\User",
"Updated":"2014-09-06T13:55:34.054Z",
"UpdatedBy":""
}
Because this is not wrapped by "value", the Kendo grid is not updating the data source correctly. The controller that does the POST or PUT, currently returns the entity as follows:
return Created(category);
OR
return Updated(category);
I was able to fix the issue by changing the response to a JsonResult as follows:
return Json(new { value = new[] { category } });
With that, everything works as desired...however, my HTTP response is now 200 (it seems that the JsonResult will always respond with 200). In a perfect world, I could return a 201 on create. Should I just accept that I have it working and live with the 200, or, is there a simple way to respond with a 201 and still format my JSON as necessary? It seems Web API 2 allowed a more customized http response, but my web api 2.2 controller actions are returning IHttpActionResult. I really don't want to create a custom class just to have a special return type and I can't seem to return my anonymous object with Created().
In summary, I'm really leaning toward just living with what I have. However, I would be interested in a way to return my anonymous object with a 201, or, a way to accept the non-"value wrapped" json in my kendo dataSource and have it update the data appropriately.
Update Since Kendo now supports ODATA V4 there is no longer any need for tweaks to make it work.
Changing the data set type from
type: 'odata'
to
type: 'odata-v4'
Should do the trick. Example source code is here
This is what I ended up doing in the end - I made my own "CreatedObject" method that would generate a ResponseMessageResult. This would wrap the object with the anonymous "value" object and serialize to json. Then, I could return this with the desired response code.
public ResponseMessageResult CreatedObject(string location, object createdObject)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
string json = serializer.Serialize(new { value = new[] { createdObject } });
// Create the response and add the 201 response code
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Created);
response.Headers.Add("Location", location);
response.Content = new System.Net.Http.StringContent(json);
// return the result
return ResponseMessage(response);
}
This solved the issue for the Kendo dataSource as the client. However, I didn't like the idea of manipulating the odata response for a particular client. So, instead, I modified the client to handle the normal Web API OData response as follows:
schema: {
data: function (data) {
if (data.value) {
return data.value;
} else {
delete data["##odata.context"];
return data;
}
},
total: function (data) { return data['##odata.count']; },
model: {
etc...
Now, the schema.data() function checks to see if the object(s) are wrapped in the "value" or not before returning the appropriate data. When returning the created object, I had to remove the #odata.context attribute as kendo didn't like it.
I like the solution of manipulating this in the client much better.
One thing I had to add is a non null PK as my odata endpoint would not except null id:
parameterMap: function (data, operation) {
if (operation == "read") {
var paramMap = kendo.data.transports.odata.parameterMap(data);
delete paramMap.$format;
delete paramMap.$inlinecount;
paramMap.$count = true;
return paramMap;
} else if (operation == "create" || operation == "update") {
//delete data["__metadata"];
if (data.Id==null) {
data.Id = 1;
}
return JSON.stringify(data);
}
},
This should be simple but I cannot seem to get this to work correctly.
Given a JSON string that looks like this:
{
"?xml":
{
"#version":"1.0",
"#encoding":"ISO-8859-1"
},
"results":
{
"title":"AOL Movies - Closest Theater and Showtimes",
// etc,
"theater":
{
"theaterId":"10650",
"id":"10650",
// etc
},
"movie":
[
{
"studios":"Warner Bros.",
"movieId":"61683"
}
]
}
I continually get undefined objects when trying to get to any value, for example:
data.movie, or data.results.title.
Everything "looks" ok in the output.
jQuery.ajax({
type: 'GET',
contentType: 'application/json',
url: 'handler.ashx',
data: 'zip=' + postalCodes.join(','),
success: function (payload) {
var data = that.objectifyJSON(payload); // this returns typeof = object
that.constructMoviesArray(data);
},
error: function (error) {
alert(error.responseText);
}
});
this.constructMoviesArray = function (data) {
var key, movie, theater = null;
var movies = {};
movies.items = {};
movies.length = 0;
alert(data[0].movie); // FAIL - there's clearly an array but nothing displays
I hope this is enough information; I am not experienced with JSON and jQuery so I'm struggling to figure out why I can't seem to resolve this.
Add the json dataType. http://api.jquery.com/jquery.ajax/
The server response is probably still a string even though you set the contentType.
jQuery.ajax({
type: 'GET',
contentType: 'application/json',
url: 'handler.ashx',
data: 'zip=' + postalCodes.join(','),
dataType: 'json', // <--- UPDATE ME
success: function (payload) {
var data = that.objectifyJSON(payload); // this returns typeof = object
that.constructMoviesArray(data);
},
error: function (error) {
alert(error.responseText);
}
});
If that doesn't help, try adding a console.log or a breakpoint to the success callback. You'll be able to take a closer look at what kind of data you are working with in payload.
success: function (payload) {
console.log(payload);
},
Hope that helps!
Try Like
var geocodingAPI = "http://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=true";
$.getJSON(geocodingAPI, function (json) {
// Set the variables from the results array
var address = json.results[0].formatted_address;
console.log('Address : ', address);
var latitude = json.results[0].geometry.location.lat;
console.log('Latitude : ', latitude);
var longitude = json.results[0].geometry.location.lng;
console.log('Longitude : ', longitude);
// Set the table td text
$('#address').text(address);
$('#latitude').text(latitude);
$('#longitude').text(longitude);
});
Demo
This is my JSON string generated by C# JSON parser:
{
"NewDataSet": {
"Table": [
{
"ResultId": "1",
"AttachmentId": "1",
"AttachmentName": "Report1",
"RowsCount": "34",
"NotifyUserName": "william",
"InsBy": "developer",
"InsAt": "2012-12-07T17:28:01.46+08:00",
"IsNotify": "false"
},
{
"ResultId": "2",
"AttachmentId": "2",
"AttachmentName": "Report2",
"RowsCount": "37",
"NotifyUserName": "william",
"InsBy": "developer",
"InsAt": "2012-12-07T17:28:15.57+08:00",
"IsNotify": "false"
},
{
"ResultId": "3",
"AttachmentId": "3",
"AttachmentName": "Report3",
"RowsCount": "69",
"NotifyUserName": "william",
"InsBy": "developer",
"InsAt": "2012-12-07T17:28:25.58+08:00",
"IsNotify": "false"
}
]
}
}
Then I would like to parse the string to front end JavaScript to iterate the value.
I did this way.
var jsonText;
$.ajax({
type: "POST",
url: "Default.aspx/MethodWithNoParameterJSON",
data: {},
contentType: "application/json;charset=utf-8",
dataType: "json",
async: true,
cache: false,
success: function (msg) {
//rulesName = dbtitle+msg.d;
//rulesCount = +msg.d;
jsonText = msg.d;
alert(jsonText.NewDataSet.Table[0].ResultId),
},
error: function (x, e) {
alert("The call to the server side failed. " + x.responseText);
}
});
How can I get the child element data like jsonText.NewDataSet.Table[0].ResultId? Whenever I call alert(jsonText.NewDataSet.Table[0].ResultId), it will always prompt null or undefined object.
Why are you using msg.d to set your jsonText variable? Where does the .d property come from? The msg parameter should already be the object created from your JSON response. Try this instead:
msg.NewDataSet.Table[0].ResultId
(And note that your jsonText variable is badly named: what you have at that point is not JSON or "text", it is an object - or, in your case, undefined because msg.d is undefined. But you are trying to use it as an object, not as JSON.)
typo? I think it should be
jsonText.NewDataSet.Table[0].ResultId // replace RulesId with ResultId
I have an action on my server side:
public void Action(Container container)
{
}
where container is:
public class Container
{
public string[] Arr;
}
From the client side, I'm invoking this action in the way below:
$.post("Controller/Action", { Arr: "abc" }, null, "json");
After this, the Arr in container contains one element "abc".
But, while invoking action the way below:
$.post("Controller/Action", { Arr: ["abc", "def"] }, null, "json");
the json array is not deserializing on the server side. The Arr in container is NULL.
How to make this simple thing work ?
Regards
If you are using jquery 1.4 try this:
$.ajax({
url: 'Controller/Action',
type: 'post',
data: { Arr: [ 'abc', 'def' ] },
traditional: true,
dataType: 'json'
});
Notice the traditional flag.
Or if you prefer to continue using $.post():
$.post(
'Controller/Action',
$.param({ Arr: [ 'abc', 'def'] }, true),
null,
'json'
);