JSON returned from ASP.NET WebService is inadequate - asp.net

I have constructed several web services that successfully serialize my .NET types into JSON. However, I'm running into an issue getting the JSON to be fully compatible with the YUI library and the DataTable in particular.
The YUI DataTable can be fully configured using JSON objects. I created the following struct in .NET to represent a given YUI column definition:
public struct YuiColumnDefinition{
public string key;
public string label;
public bool sortable;
public string formatter;
}
The 'formatter' property is how you instruct the YUI table to use a custom javascript function when displaying a given column. The problem is that, since formatter is defined as a String, ASP.NET wraps it's value in double quotes upon serialization, and YUI no longer recognizes the value as a JavaScript token:
JSON YUI expects
[
{key:"1", label:"Order Date", formatter:myCustomJavaScriptFunction, sortable:true},
{key:"2", label:"Customer Name", formatter:null, sortable:false}
]
JSON ASP.NET creates
[
{key:"1", label:"Order Date", formatter:"myCustomJavaScriptFunction", sortable:true},
{key:"2", label:"Customer Name", formatter:null, sortable:false}
]
Anyone have a solution outside of modifying the YUI source code?
Thanks

Change your parsing code.
Think of json as a replacement for xml, you wouldnt put a variable/function in xml. In either, you could easily identify the name or type (say from a list or enum) of the formatter to use. Then your parsing code would know that it should assign a variable/method as the "formatter" property.
Its just incorrect to return an actual variable/function in a callback like that. You could make it work but honestly its not the way to go.
I would do the following...
Change your return json to this.
[
{key:"1", label:"Order Date", formatterName:"myCustomJavaScriptFunction", sortable:true},
{key:"2", label:"Customer Name", formatterName:null, sortable:false}
]
Then in JS, lets assume that json is stored in variable returnedObj
function augmentReturnedObject(returnedObj)
{
// validate that returnObj.formatterName (as a variable) is not undefined
var isValidObj = (window[returnedObj.formatterName] !== undefined);
if (isValidObj)
{
// this will return the actual function / variable, here we are assigning it to returnedObj.formatter
returnedObj.formatter = window[returnedObj.formatterName];
}
else
{
returnedObj.formatter = null;
}
}
You could easily reduce that down to this without much thought
function augmentReturnedObject(returnedObj)
{
var specifiedMethod = window[returnedObj.formatterName];
returnedObj.formatter = (specifiedMethod === undefined) ? null : window[returnedObj.formatterName];
}
So in the end you'd take your json object, and do augmentReturnedObject(returnedObj); and at that point you can pass returnedObj to YUI

As Allen correctly pointed out:
Its just incorrect to return an actual variable/function in a callback like that.
Still, I couldn't find anywhere in the YUI documentation that spells out how to deal with javascript functions returned as strings from either JSON or XML.
Thank goodness for blogs. As pointed out in this one, the "proper" way to register custom javascript formatting functions is by using YAHOO.widget.DataTable.Formatter:
JSON Column Definitions as returned from .ASMX
[
{key:"1", label:"Order Date", formatter:"myCustomJavaScriptFunction1", sortable:true},
{key:"2", label:"Customer Name", formatter:null, sortable:false}
{key:"3", label:"State", formatter:"myCustomJavaScriptFunction2", sortable:false}
]
Javscript to wire up the YUI DataTable
YAHOO.widget.DataTable.Formatter.myCustomJavaScriptFunction1= this.myCustomJavaScriptFunction1;
YAHOO.widget.DataTable.Formatter.myCustomJavaScriptFunction2= this.myCustomJavaScriptFunction2;
function myCustomJavaScriptFunction1(elCell, oRecord, oColumn, oData) {
//do something
}
function myCustomJavaScriptFunction2(elCell, oRecord, oColumn, oData){
//do something
}

Related

CamelCase property names with NJsonSchema C# CodeGeneration

does anybody know a way to configure NJsonSchema to use CamelCase property naming durching code generation? I've a JSON schema with property names like message_id which lead to C# property name 'Message_id' where i.e. 'MessageId' whould be a more C#-like way.
With an attribute like '[JsonProperty("message_id"]' it would be no problem to specified the connection between the different names.
So, you asked about code generation. I was having trouble with the schema it generated not matching what was getting sent to my Angular app. So, while this isn't exactly what you were looking for, perhaps it helps you find an answer (maybe?).
To generate the schema with the camel case property names, I'm setting the Default Property Name Handling to CamelCase, but this is using the deprecated call to set these settings directly. There should be some way to use the SerializerSettings directly, but I wasn't quite able to make that work. This isn't production code for me, so it will do.
internal class SchemaFileBuilder<T>
{
public static void CreateSchemaFile()
{
CreateSchemaFile(typeof(T).Name);
}
public static void CreateSchemaFile(string fileName)
{
JsonSchemaGeneratorSettings settings = new JsonSchemaGeneratorSettings();
settings.DefaultPropertyNameHandling = PropertyNameHandling.CamelCase;
var schema = NJsonSchema.JsonSchema.FromType<T>(settings);
var json = schema.ToJson();
Directory.CreateDirectory("Schemas");
File.WriteAllText($"Schemas\\{fileName}.schema.json", json);
}
}
I set this up as a generic function so I could pass multiple schemas in to either createSchemaFile functions. Here's are some example calls which would generate a Person.schema.json file and a Persons.schema.json file:
SchemaFileBuilder<Person>.CreateSchemaFile();
SchemaFileBuilder<Dictionary<string, Person>>.CreateSchemaFile("Persons");

Return JSON via MVC controller vs webmethod

I'm trying to convert a web forms page with a number of code-behind webmethod functions into an MVC view with a controller.
The data I get back is different. They have properly formatted JSON, but the webmethod returns JSON like this:
{"d":"{\"Success\":true,\"Data\":{\"QuoteId\":340439,\"LoginId\":40,
And the controller returns:
"{\"QuoteId\":340444,\"LoginId\":40,
Its not wrapping it in data.d like it is set up to handle in the javascript and there are no Success or Data objects. And when I try to parse it ($.parseJSON(data)) like I did with the webmethod, it gives me the old error at line 1 message.
I'm sure that if I played with it enough I could get it to work with the way the data is coming through, but I have many pages that I need to covert in the future and I'm just wondering if there is an easy way to get the controller to format it like the webmethod does.
I'm pretty sure that I know why the data is formatted differently, but it would make my life easier if I could just return the data in the same way. I've tried returning a JsonConvert.SerializeObject(obj), which is just a string, and a Return Json(obj), which I'm guessing is just a string as well, but they both return stuff the same, non-data.d way.
Okay, so I've decided to wrap the data in a "d", so that I can have a "data.d" in the Ajax result.
But for some reason the dates aren't being formatted correctly when I just send the data back via:
return Json(thewrappeddata);
they turn out weird.
So basically what I have to do is this:
public class JSONReturn {
public object d;
public static object Wrap(object data) {
return new JSONReturn() { d = data };
}
}
var thedata = new TonsofData();
return Json(
JSONReturn.Wrap(
JsonConvert.SerializeObject(thedata,
new JsonSerializerSettings {
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
}
)
)
);
It seems like I'm "jsonifying" the data twice here, once with JsonConvert.SerializeObject() and once with Json() (I THINK that serializes), but it seems to work and now I get what I need and all the JS just works as it did before. If I leave out the JsonConvert.SerializeObject(), the Json() formats the dates incorrectly and it causes all sorts of failure down the line.

How do I pass object (ObjectProxy) from Flex back to .NET WebService?

So, there are a wealth of Flex articles online about how to handle a .NET WebMethod that returns a DataSet or DataTable. Here is an example:
Handling web service results that contain .NET DataSets or DataTables
So, I know how to use result.Tables.<tablename>.Rows and the like. But what I cannot seem to figure out or find online is how to go the other direction - a method to pass objects or tables back to the .NET Webservice from Flex, without stooping to passing XML as a string, or making huge web service methods that have one parameter for each property/column of the object being stored. Surely others, smarter than I, have tackled this issue.
I am using ASP.NET 2.0 Typed DataSets, and it would be really nice if I could just pass one object or array of objects from Flex to the web service, populate my Typed DataTable, and do an Update() through the corresponding typed TableAdapter. My dream would be a [WebMethod] something like one of these:
public void SaveObject(TypedDataTable objToSave) { ... }
public void SaveObject(TypedDataSet objToSave) { ... }
I've had the typed datatables saving to the database, I know how to do that part and even a few tricks, but we had XML being passed back-and-forth as a string - eww. I'm trying to get to a more object-based approach.
The best object based approach is AMF. I assume its probably a bit late in your your development cycle to change your integration layer, but otherwise I dont know of a way to get around marshalling your object(s) back into XML or separating them out into their primitive components.
For .NET implementations of AMF check out:
FlourineFX(FOSS)
WebORB for .NET
Its amazing how easy things become once AMF is used, for example using the Mate MVC framework and an AMF call passing a complex object to the server looks something like this:
<mate:RemoteObjectInvoker instance="yourWebservice" method="saveComplexObject" showBusyCursor="true" >
<mate:resultHandlers>
<mate:CallBack method="saveComplexObjectSuccess" arguments="{[resultObject]}" />
</mate:resultHandlers>
<mate:faultHandlers>
<mate:MethodInvoker generator="{DataManager}" method="presentFault" arguments="{fault}" />
</mate:faultHandlers>
</mate:RemoteObjectInvoker>
With result and fault handlers being optional.
The direction I ended up going was close to what I hoped was possible, but is "hack-ish" enough that I would consider SuperSaiyen's suggestion to use AMF/ORM a better solution for new/greenfield projects.
For sake of example/discussion, let's say I am working with a Person table in a database, and have a typed DataSet called PeopleDataSet that has PersonTableAdapter and PersonDataTable with it.
READ would look like this in .NET web service:
[WebMethod]
public PeopleDataSet.PersonDataTable GetAllPeople() {
var adapter = new PersonTableAdapter();
return adapter.GetData();
}
... which in Flex would give you a result Object that you can use like this:
// FLEX (AS3)
something.dataProvider = result.Tables.Person.Rows;
Check out the link I put in the question for more details on how Flex handles that.
CREATE/UPDATE - This is the part I had to figure out, and why I asked this question. The Flex first this time:
// FLEX (AS3)
var person:Object = {
PersonID: -1, // -1 for CREATE, actual ID for UPDATE
FirstName: "John",
LastName: "Smith",
BirthDate: "07/19/1983",
CreationDate: "1997-07-16T19:20+01:00" // need W3C DTF for Date WITH Time
};
_pplWebSvcInstance.SavePerson(person); // do the web method call
(For handling those W3C datetimes, see How to parse an ISO formatted date in Flex (AS3)?)
On the .NET web service side then, the trick was figuring out the correct Type on the web method's parameter. If you go with just Object, then step into a call with a debugger, you'll see .NET figures it is a XmlNode[]. Here is what I figured out to do:
[WebMethod]
public int SavePerson(PeopleDataSet p) {
// Now 'p' will be a PeopleDataSet with a Table called 'p' that has our data
// row(s) (just row, in this case) as string columns in random order.
// It WILL NOT WORK to use PeopleDataSet.PersonDataTable as the type for the
// parameter, that will always result in an empty table. That is why the
// LoadFlexDataTable utility method below is necessary.
var adapter = new PersonTableAdapter();
var tbl = new PeopleDataSet.PersonDataTable();
tbl.LoadFlexDataTable(p.Tables[0]); // see below
// the rest of this might be familiar territory for working with DataSets
PeopleDataSet.PersonRow row = tbl.FirstOrDefault();
if (row != null) {
if (row.PersonID > 0) { // doing UPDATE
row.AcceptChanges();
row.SetModified();
}
else { // doing CREATE
row.CreationDate = DateTime.UtcNow; // set defaults here
row.IsDeleted = false;
}
adapter.Update(row); // database call
return row.PersonID;
}
return -1;
}
Now, the kluge utility method you saw called above. I did it as extension method, that is optional:
// for getting the Un-Typed datatable Flex gives us into our Typed DataTable
public static void LoadFlexDataTable(this DataTable tbl, DataTable flexDataTable)
{
tbl.BeginLoadData();
tbl.Load(flexDataTable.CreateDataReader(), LoadOption.OverwriteChanges);
tbl.EndLoadData();
// Probably a bug, but all of the ampersand (&) in string columns will be
// unecessarily escaped (&) - kluge to fix it.
foreach (DataRow row in tbl.Rows)
{
row.SetAdded(); // default to "Added" state for each row
foreach (DataColumn col in tbl.Columns) // fix & to & on string columns
{
if (col.DataType == typeof(String) && !row.IsNull(col))
row[col] = (row[col] as string).Replace("&", "&");
}
}
}

Flex: AMF and Enum Singletons – can they play well together?

I'm using Python+PyAMF to talk back and forth with Flex clients, but I've run into a problem with the psudo-Enum-Singletons I'm using:
class Type {
public static const EMPTY:Type = new Type("empty");
public static const FULL:Type = new Type("full");
...
}
When I'm using locally created instances, everything is peachy:
if (someInstance.type == Type.EMPTY) { /* do things */ }
But, if 'someInstance' has come from the Python code, it's instance of 'type' obviously won't be either Type.EMPTY or Type.FULL.
So, what's the best way to make my code work?
Is there some way I can control AMF's deserialization, so when it loads a remote Type, the correct transformation will be called? Or should I just bite the bullet and compare Types using something other than ==? Or could I somehow trick the == type cohesion into doing what I want?
Edit: Alternately, does Flex's remoting suite provide any hooks which run after an instance has been deserialized, so I could perform a conversion then?
Random thought: Maybe you could create a member function on Type that will return the canonical version that matches it?
Something like:
class Type {
public static const EMPTY:Type = new Type("empty");
public static const FULL:Type = new Type("full");
...
// I'm assuming this is where that string passed
// in to the constructor goes, and that it's unique.
private var _typeName:String;
public function get canonical():Type {
switch(this._typeName) {
case "empty": return EMPTY;
case "full": return FULL;
/*...*/
}
}
}
As long as you know which values come from python you would just convert them initially:
var fromPython:Type = /*...*/
var t:Type = fromPython.canonical;
then use t after that.
If you can't tell when things come from python and when they're from AS3 then it would get pretty messy, but if you have an isolation layer between the AS and python code you could just make sure you do the conversion there.
It's not as clean as if you could control the deserialization, but as long as you've got a good isolation layer it should work.

best way to store / lookup name value pairs

I have a list of error codes I need to reference, kinda like this:
Code / Error Message
A01 = whatever error
U01 = another error
U02 = yet another error type
I get the Code returned to me via a web service call and I need to display or get the readable error. So I need a function when passed a Code that returns the readable description. I was just going to do a select case but thought their might be a better way. What is the best way / most effieient way to do this?
Use a Dictionary, (in C#, but the concept and classes are the same):
// Initialize this once, and store it in the ASP.NET Cache.
Dictionary<String,String> errorCodes = new Dictionary<String,String>();
errorCodes.Add("A01", "Whatever Error");
errorCodes.Add("U01", "Another Error");
// And to get your error code:
string ErrCode = errorCodes[ErrorCodeFromWS];
You would use a dictionary. A dictionary uses a hashmap internally for performance, so it is good in that regard. Also, because you want this to go as quickly as possible by the sounds of it, I would statically initialize it in its own class instead of, for example, in an XML file or slimier. You would probably want something like:
public static class ErrorCodes
{
private static Dictonary<string, string> s_codes = new Dicontary<string, string>();
static ErrorCodes()
{
s_codes["code"] = "Description";
s_codes["code2"] = "Description2";
}
public static string GetDesc(string code)
{
return s_codes[code];
}
}
That way, if you wanted to move the back end to a file instead of being static, then you could.

Resources