javascript : pass an array to WCF service (c#) - c#

I am trying to send an array to a wcf service.
My javascript :
var listoffice = new Array();
var office1 = { officeid : "1", officename : "Bat Cave" };
var office2 = { officeid : "2", officename : "Robin House" };
listoffice[0] = office1;
listoffice[1] = office2;
$.getJSON("ContactService.svc/createnewAwesomeoffice", { listoffice: listoffice }, function (data) {
...
});
Here's the service :
public struct officetoadd
{
public string officeid;
public string officename;
}
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json)]
public void createnewAwesomeoffice(List<officetoadd> listoffice)
{
...
}
the problem is the listoffice(in the service) is always null. Am I missing something ?

I think you need to put RequestFormat as well in your WebGetAttribute... Also, you may want to try turning "officetoadd" into a class and decorate it with DataContract and DataMember Attributes.
[DataContract]
public class officetoadd
{
[DataMember]
public string officeid;
[DataMember]
public string officename;
}
[OperationContract]
[WebGet(RequestFormat - WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
public void createnewAwesomeoffice(List<officetoadd> listoffice)
{
...
}

Ok, I found a solution by myself. I think this is pertinent to write it here, so it may help other people.
I simply had to convert my array to JSON with this :
var arrayjson = JSON.stringify(listoffice);
And then pass it to the WCF service :
$.getJSON("ContactService.svc/createnewAwesomeoffice", { listoffice: arrayjson }, function (data) {
...
});
Note: The JSON object is now part of most modern web browsers (IE 8 & above).

Related

JSON Restful service. Could not cast or convert from System.String to System.Collections.Generic.List

I have a problem serializing and deserializing json. I have searched for an answer and although Ive seen the same problem elsewhere, non of the answers have helped me. I'm having the same problem using newtonsoft or the javascriptserializer.
My web method in the rest service
[OperationContract(Name="Customers")]
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "json/Customer/Read/{CustomerID}/{CustomerCode}/{ParentCustomerID}/{CustomerName}")]
String JSONReadCustomers(String CustomerID, String CustomerCode, String ParentCustomerID, String CustomerName);
The class
public class Customer
{
public Int32 CustomerID { get; set; }
public String CustomerCode { get; set; }
public String CustomerName { get; set; }
public Customer(DataRow DR)
{
CustomerID = Convert.ToInt32(DR["CustomerID"]);
CustomerCode = (DR["CustomerCode"] != null) ? DR["CustomerCode"].ToString() : "";
CustomerName = (DR["CustomerName"] != null) ? DR["CustomerName"].ToString() : "";
}
}
the bit that actually does the serializatiion
private String GetJSON(DataTable DT)
{
try
{
List<Customer> customers = new List<Customer>();
foreach (DataRow DR in DT.Rows)
{
customers.Add(new Customer(DR));
}
return JsonConvert.SerializeObject(customers);
}
catch
{
throw;
}
}
So far everything seems ok.
The service compiles and runs ok. when I test it in a browser I get the following result
"[{\"CustomerID\":1,\"CustomerCode\":\"AMT-1\",\"CustomerName\":\".AMTDisp\"},{\"CustomerID\":2,\"CustomerCode\":\"COM-2\",\"CustomerName\":\".ComexDisp,_\"}]"
I have a VB test harness that I am using call the rest service and deserialize the returned json
The Class
Public Class Customer
Public CustomerID As Int32
Public CustomerCode As String
Public CustomerName As String
End Class
the method
Private Function DeserializeJSON() As List(Of Customer)
Dim request As WebRequest = WebRequest.Create(GetCustomerURL())
request.Credentials = CredentialCache.DefaultCredentials
Dim response As WebResponse = request.GetResponse()
Dim dataStream As Stream = response.GetResponseStream()
Dim reader As New StreamReader(dataStream)
Dim responseJSON As String = reader.ReadToEnd()
Dim customers As List(Of Customer) = JsonConvert.DeserializeObject(Of List(Of Customer))(responseJSON)
Return customers
End Function
The Error
{"Could not cast or convert from System.String to System.Collections.Generic.List`1[RESTTestHarness.Customer]."}
I have used a variety of different ways such as setting the bodystyle to Wrapped and having a root object. Nothing seems to work. I always get the same error. I believe the error is really simple here, but I just can't see it now.
if you want to return a generic json response, you need to change your service operation contract to return a Message not a string,
your service operation implementation should then look something like:
public System.ServiceModel.Channels.Message CreateJsonResponse()
{
List<Customer> response = GetCustomers();
string msg = JsonConvert.SerializeObject(response);
return WebOperationContext.Current.CreateTextResponse(msg, "application/json; charset=utf-8", Encoding.UTF8);
}

Return a string to a custom object type

I am trying to write a C# restful web service which returns the customer name when account number is passed as a parameter.
I have a customer class:
public class Customer_T
{
public string CustName { get; set; }
}
An interface class:
[ServiceContract(Namespace = "", Name = "CustomerInfoService")]
public interface CustomerInfo_I
{
[OperationContract]
Customer_T CustName(string accountno);
}
*Another class called CustomerInfo which implements the CustomerInfo_I interface:*
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class CustomerInfo : CustomerInfo_I
{
[WebInvoke(Method = "GET", UriTemplate = "customer/{accountno}", ResponseFormat = WebMessageFormat.Json)]
public Customer_T CustName(string accountno)
{
string custName = "";
CustNameByAccountNo custdb = new CustNameByAccountNo();
custName = custdb.getCustName(accountno).ToString();
if (custName.Equals("") == false)
{
return new Customer_T { CustName = custName };
}
else
{
return null; //This is where I want to change
}
}
}
Instead of returning null, I would like to return a string "InvalidAccNo".
I did try it, but it gave me an error.
Cannot implicitly convert type 'string' to 'CustomerInfo.Service.Customer_T'
You won't be able to return a string since your return type is defined as a Customer_T. One option you might want to consider is to take a look at fault handling inside of WCF to return a fault to a user rather than repurposing a single object. Throwing the fault can be used in indicate that an invalid account number was specified.
[ServiceContract(Namespace = "", Name = "CustomerInfoService")]
public interface CustomerInfo_I
{
[OperationContract]
[FaultContract(typeof(string))]
Customer_T CustName(string accountno);
}
http://msdn.microsoft.com/en-us/library/ms733721.aspx
Also, your Customer class, you should mark the class with the DataContract attribute and your data members with DataMember in order to return complex types.
[DataContract]
public class Customer_T
{
[DataMember]
public string CustName { get; set; }
}
Is this what you want?
if (custName.Equals("") == false)
{
return new Customer_T { CustName = custName };
}
else
{
return new Customer_T { CustName = "Invalid Customer Name!" };
}
Although it sounds like you might want a property in Customer_T of IsValid, or something like that. Set it to false in the 2nd case, and override ToString of Customer_T:
public override string ToString()
{
return this.IsValid ? this.CusName : "Invalid Customer Name!";
}
Then you can do:
if (custName.Equals("") == false)
{
return new Customer_T { CustName = custName };
}
else
{
return new Customer_T { IsValid = false };
}
Just make sure you're setting IsValid to true in the Customer_T constructor, so it is valid by default.
The method is declared to return a Customer_T, which your string is not an instance of. I would advise against it but if you really want to make a method that results an instance of an object OR a string, you could have it return object. Which both Customer_T and string are.
Change
public Customer_T CustName(string accountno)
Into:
public object CustName(string accountno)
The problem with this is that now the caller of the method doesn't know for sure what it will result in.
The compiler is right.
In your Interface, you have declared that CustName returns a Customer_T, but now you have changed your mind and need a string as return value.
That's not good from a webservice contract standpoint.
So if you need the Customer_T as return value don't change anything and throw an exception if the custname is not found. Alternatives are
Add a new method with the appropriate signature
Change (Breaking change) your contract in a way that CustName returns
a string.

Deserialize javascript objects to generic list in c# handler

Im creating some javascript items in a loop
var licenseList = {};
$($licenses).each(function (index) {
var license = {};
var editedValues = {};
license.PRODUCT_KEY = $(this).parent('div.licensewrapper').data('productkey');
$(this).find('input:text').each(function (i, val) {
if ($(val).attr('data-default-value') != $(val).val() && $(val).val() > 0 && $(val).data('isValid') != false) {
var pogKey = $(val).data('product_option_group_key');
var editedValue = $(val).val();
editedValues[pogKey] = editedValue;
license.editedValues = editedValues;
}
});
//licenseList[index] = license;
//liceneList.push(license); //if array...
});
I've commented out my current solutions. But i dont think any of the two are equal to a generic list when derelializing them in c#. Whats the corrent way to do it in this case? Thanks
create your array
var licenseList = [];
for each of your licenses...
var license = {
prop1: 'p1',
prop2: 'p2'
};
licenseList.push(license);
format and serialize JSON data to be sent over to webmethod
data = {
LicenseList: licenseList
};
$.ajax({
...
data: JSON.stringify(data)
...
});
in your webmethod your method parameter must match
[WebMethod]
public static string GetLicenses(List<License> LicenseList)
{
foreach(var license in LicenseList)
{
// do whatever
}
}
sample license class. Your properties need to match with your objects in your javascript
public class License
{
public string Prop1 {get; set;}
public string Prop2 {get; set;}
}
Hope this helps.
function sendToServer(licenseList)
{
var url = "controller.ashx";
var data = {};
data.SerializedObj = JSON.stringify(licenseList);
$.post(url, data, function(response){
if(response.length > 0)
{
alert(response);
}
});
}
//controller.ashx :
public void ProcessRequest (HttpContext context) {
//...
string serializedObj = context.Request.Form["SerializedObj"] ?? "";
JavaScriptSerializer js = new JavaScriptSerializer();
List<license> collection = js.Deserialize<List<license>>(serializedObj);
//...
public class license
{
public string Propertie1 {get;set;}
public string Propertie2 {get;set;}
}
Javascript object must have the same properties:
var license = {Propertie1 : 'value1', Propertie2 : 'value2'};

Sending Form Parameters to Web-service using Operations

Client-Side I'm trying to capture the fields like this:
// Initialize the object, before adding data to it.
// { } is declarative shorthand for new Object().
var NewSubscriber = { };
NewSubscriber.FirstName = $("#FirstName").val();
NewSubscriber.LastName = $("#LastName").val();
NewSubscriber.Email = $("#Email").val();
NewSubscriber.subscriptionID = $("#subscriptionID").val();
NewSubscriberNewPerson.Password = "NewPassword1";
NewSubscriber.brokerID = "239432904812";
// Create a data transfer object (DTO) with the proper structure.
var DTO = { 'NewSubscriber' : NewSubscriber };
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "NewSubscriberService.asmx/AddDigitalSubscriber",
data: JSON.stringify(DTO),
dataType: "json"
});
Now here's the problem I'm running into. How do I send these parameters and set them in the web service using C# or vb.net if it's easier? Any help is greatly appreciated
Here is what I have so far:
public class Service1 : System.Web.Services.WebService
{
public SubNameSpace.WebServiceSubBook.drmProfile _profile;
[WebMethod]
public string SetProfileData (object DTO) {
SetProfile (DTO);
}
[WebMethod]
public class SetProfileData (SubNameSpace.WebServiceSubBook.drmProfile _profile;) {
this._profile = _profile;
return "success";
}
}
}
SetProfile is an operation within the web service. SetProfileRequest is the message in the operation. I want to set certain parameters and then list other parameters in the code-behind file such as:
access_time = 30;
I'm completely lost...help!
Front-End Coder Lost in C# Translation
Your javascript object's first 'head' id NewSubscriber must match your web methods signature, for example:
you are calling url: "NewSubscriberService.asmx/AddDigitalSubscriber", with var DTO = { 'NewSubscriber' : NewSubscriber };
So you need something like this:
[WebMethod]
public string AddDigitalSubscriber(NewSubscriber NewSubscriber)
{
string status = string.Empty;
// Add Subscriber...
string emailFromJavascript = NewSubscriber.Email;
// do something
return status;
}
You will need a class in .NET like the following to match with your javascript object:
//... Class in .NET
public class NewSubscriber
{
public string FirstName;
public string LastName;
public string Email;
public int subscriptionID;
public string Password;
public string brokerID;
}
So the objects match and the names match.

ASMX Seems To Cache

I have the following ASMX web service:
// removed for brevity //
namespace AtomicService
{
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[ScriptService]
public class Assets : WebService
{
private static readonly ILog Log = LogManager.GetLogger(typeof(Validation));
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public string CityCreate(string cityname, string state, string country,
decimal timezoneoffset, decimal lat, decimal lon)
{
var response = new Dictionary<string, string>();
if(string.IsNullOrEmpty(cityname) || (string.IsNullOrEmpty(country)))
{
response.Add("Response", "empty");
return JsonConvert.SerializeObject(response, Formatting.Indented);
}
int tzid;
int ctyid;
try
{
tzid = AtomicCore.TimezoneObject.GetTimezoneIdByGMTOffset(timezoneoffset);
var cty = AtomicCore.CountryObject.GetCountry(country);
ctyid = cty.CountryId;
}
catch (Exception)
{
response.Add("Response", "errordb");
return JsonConvert.SerializeObject(response, Formatting.Indented);
}
if(AtomicCore.Validation.DoesCityAlreadyExistByLatLon(cityname, country, lat, lon))
{
response.Add("Response", "exists");
return JsonConvert.SerializeObject(response, Formatting.Indented);
}
const string pattern = #"^(?<lat>(-?(90|(\d|[1-8]\d)(\.\d{1,6}){0,1})))\,{1}(?<long>(-?(180|(\d|\d\d|1[0-7]\d)(\.\d{1,6}){0,1})))$";
var check = new Regex(pattern, RegexOptions.IgnorePatternWhitespace);
bool valid = check.IsMatch(lat + "," + lon);
if(valid == false)
{
response.Add("Response", "badlatlon");
return JsonConvert.SerializeObject(response, Formatting.Indented);
}
BasicConfigurator.Configure();
Log.Info("User created city; name:" + cityname + ", state:" + state + ", countryid:" + ctyid + ", timezoneid:" + tzid + ", lat:" + lat + ", lon:" + lon + "");
//will return id of created city or 0.
var result = AtomicCore.CityObject.CreateCity(cityname, state, ctyid, tzid, lat.ToString(), lon.ToString(), string.Empty);
response.Add("Response", result > 0 ? "True" : "errordb");
return JsonConvert.SerializeObject(response, Formatting.Indented);
}
}
}
This is called by a JQuery $.ajax call:
$.ajax({
type: "POST",
url: "http://<%=Atomic.UI.Helpers.CurrentServer()%>/AtomicService/Assets.asmx/CityCreate",
data: "{'cityname':'" + $('#<%=litCity.ClientID%>').val() + "','state':'" + $('#<%=litState.ClientID%>').val() + "','country':'<%=Session["BusinessCountry"]%>','timezoneoffset':'" + $('#<%=litTimezone.ClientID%>').val() + "','lat':'" + $('#<%=litLat.ClientID%>').val() + "','lon':'" + $('#<%=litLng.ClientID%>').val() + "'}",
contentType: "application/json",
dataType: "json",
success: function (msg) {
if (msg["d"].length > 0) {
var data = $.parseJSON(msg.d);
if (data.Response > 0) {
//everything has been saved, ideally we
//want to get the cityid and pass it back
//to the map page so we can select it...
alert('Hazzah!');
$(this).dialog('close');
} else {
if(data.Response == 'errordb')
{
alert("db");
}
if(data.Response == 'exists')
{
alert("exists");
}
if(data.Response == 'badlatlon')
{
alert("badlatlon");
}
if(data.Response == 'empty')
{
alert("empty");
}
$('#serviceloader').hide();
$('#<%=txtFindMyCity.ClientID%>:input').attr('disabled', false);
$('#erroroccured').show('slow');
$('#errortext').html("Unfortunately, we can't save this right now. We think this city may already exist within Atomic. Could you please check carefully and try again? If this is an obvious error, please contact us and we'll get you started.");
}
} else {
$('#serviceloader').hide();
$('#<%=txtFindMyCity.ClientID%>:input').attr('disabled', false);
$('#erroroccured').show('slow');
$('#errortext').html("Unfortunately, we can't save this right now. Our data service is not responding. Could you perhaps try again in a few minutes? We're very sorry. Please contact us if this continues to happen.");
}
},
error: function (msg) {
$('#serviceloader').hide();
$('#<%=txtFindMyCity.ClientID%>:input').attr('disabled', false);
$('#erroroccured').show('slow');
$('#errortext').html("Unfortunately, we can't save this right now. Perhaps something has gone wrong with some of our data services. Why not try again? If the problem persists, please let us know and we'll get you started.");
}
});
And despite this, I always receive { "Response" : "False" }. I think this may be a cache problem - but I simply can't get the AJAX to run the service. When I go directly to asset.asmx and invoke the service, the CreateCity method runs correctly.
This is a classic wood for the trees - I have looked at it too long and I'm giving up the will to live...
Thus, the question is: I'd like the CreateCity service work correctly, when called by $.ajax, and not receive a { "Response" : "False" } response. Can anyone provide any direction, help or assistance on how to achieve this with the code I have provided? Please bear in mind that I believe that my service may be caching (hence the { "Response" : "False" } response)...
Help, advice, flames and general comments welcomed...
You don't need to manually JSON serialize the response. This is automatically handled by script enabled services. Simply return a strongly typed object from your web method. Same for the input. Also you must ensure to properly URL encode the request parameters. So start by defining models:
public class City
{
public string Cityname { get; set; }
public string State { get; set; }
public string Country { get; set; }
public decimal Timezoneoffset { get; set; }
public decimal Lat { get; set; }
public decimal Lon { get; set; }
}
public class Response
{
public string Message { get; set; }
public bool IsSuccess { get; set; }
}
and then:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[ScriptService]
public class Assets : WebService
{
private static readonly ILog Log = LogManager.GetLogger(typeof(Validation));
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public Response CityCreate(City city)
{
var response = new Response();
// ... process and populate the return message,
// set the IsSuccess boolean property which will
// be used by the client (see next...)
return response;
}
}
and on the client side:
$.ajax({
type: 'POST',
url: '<%= ResolveUrl("~/Assets.asmx/CityCreate") %>',
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify({
cityname: $('#<%=litCity.ClientID%>').val(),
state: $('#<%=litState.ClientID%>').val(),
country: '<%=Session["BusinessCountry"]%>',
timezoneoffset: $('#<%=litTimezone.ClientID%>').val(),
lat: $('#<%=litLat.ClientID%>').val(),
lon: $('#<%=litLng.ClientID%>').val()
}),
success: function (result) {
// result.d will represent the response so you could:
if (result.d.IsSuccess) {
...
} else {
...
}
};
});
You can add cache:false to your %.ajax call to make sure it isn't being cached. Have you popped a breakpoint on the service to double check what's being passed through from your jQuery?

Categories

Resources