JSON to Knockout serializer on server - c#

I am using Web API in a current project. It returns a json string which is than converted to a knockout-observable by using the knockout-mapping plugin. I am wondering if this could not be done before, on the server side. For example:
Now a GET call to \Product returns the following:
{ Id: 2, Name: "Apple", Categories: ["fruit"] }
What I like to have directly returned by the server:
var data={
Id: ko.observable(2),
Name: ko.observable("Apple"),
Categories: ko.observableArray([
ko.observable("fruit")
])
};
Is there any .NET lib to do this? Or is there any way to extend the JSON.NET lib?

Although you could use an OData formatter, it could get difficult to maintain if your front end changes. You could essentially do the same thing on your front end by creating a viewmodel from your server's JSON:
function ViewModel(data)
{
this.Id = ko.observable(data.Id);
this.Name = ko.observable(data.Name);
this.Categories = ko.observableArray(data.Categories);
}
This way, as pointed out by Shane, you wouldn't have your back end tightly coupled with Knockout. Also, as a little side note to your categories, I wouldn't recommend setting each individual item in your array to an observable unless you need to observe each item.

Related

Transforming JSON template based on JSON values posted into API

I have got a requirement of building an azure function which transforms data in our COSMOS DB based on the value posted by a calling service.
So consider this JSON template
{
"processExecutionId": 1000,
"processId": "$PID",
"parentProcessId": 10,
"objectNameTag": "$ObjectName",
"objectName": "bbps.fx",
"processStartedOn": "$startedOn",
"processCompletedOn": "$CompletedAt",
"processExecutionStatusId": 2,
"configurationPayload": "Actual Config Payload with replaced values by the orchestrator or payload read service",
"objectMetadata": {
"jsonPayLoadRead": {
"messages": "$Message"
},
"writeToDB": "$IsWrite"
}
}
This is something we in COSMOS corresponding to a key.
So when a requester application posts that Key it has a JSON object in its body like this
{
"processStartedOn": "2022-01-25 10:10:32",
"processCompletedOn": "2022-01-25 10:20:25",
"objectMetadata": {
"jsonPayLoadRead": {
"messages": "Data uploaded"
},
"writeToDB": "True"
}
}
So the method posting parameters for the template expecting a response from the API after replacing those variables in the template with the values.
The JSON template is not always the same structure. Otherwise we can atleast define a class and can deserialize into it. So what ever the key posted our service needs to take the corresponding JSON template from COSMOS and do the transformation.
So dont know how to handle it the best way or is there is any way to handle this process within COSMOS itself rather than trying using C#.
Please share your thoughts
You can always add a TemplateType property in your class so that you know the various options you have and based on that you know what class to use, there are also ways to stream from cosmos directly without using classes at all but it depends a little what you are trying to achieve if you just need to hand over to another app or not then you can stream from cosmos without even knowing what the structure is

Proper way to pass date objects between a javascript/typescript frontend and a ASP.net Core 5 backend

I'm developing a web application where the backend is a REST web API written in ASP.net Core 5 and the frontend is an Angular application written in TypeScript.
One of my ASP.net backend APIs returns an instance of this C# object:
public class MyClass
{
DateTime expires {get; set;}
string ticket {get; set;}
}
In my Angular app, I call this API with Angular's HTTP Client and deserialize the result as an instance of the following TypeScript class:
export class MyClass
{
expires: Date;
ticket: string;
}
However, this doesn't work properly because because if I inspect the TypeScript object once it has been returned I can see that the expires field actually contains a string, not a Date object.
I imagine this happens because the data travels between backend and frontend in JSON format, which doesn't support types but only knows about strings and numbers, so the C# DateTime object is converted to a string.
My question is: what is the best way to handle this problem? I would like to keep my objects strongly typed if possible... is there some configuration I can do on the ASP.net and/or Angular side so that this kind of serialization works automatically? Or if not, what is the next best approach?
This is the solution I put together from the suggested answers in the comments:
return this.http.get<MyClass>(restUrl, { observe: 'response' })
.pipe(map(response => {
if (response.ok) {
response.body.expires = new Date(response.body.expires);
this.setLoginResult(response.body);
}
return response;
}));

How to allow configurable deserialization of JObject?

In a .NET Core Console Application I'm calling a REST API that returns JSON. I'm using Newtonsoft.Json.Linq.JObject for deserialization/retrieval of values like this:
string responseJson = await Utils.GetJsonFromApi(apiEndpoint);
dynamic jObject = new Newtonsoft.Json.Linq.JObject();
jObject = NewtonSoft.Json.JsonConvert.DeserializeObject(responseJson);
foreach (var car in jObject.items)
{
string carId = car.id.ToString();
}
If the API developer changes the array variable items to list or the variable id to code, I will have to update my code and recompile/republish.
Is it possible to leverage app settings somehow, like so:
"carAPI":{
"uri": "someuri",
"itemsArrayVariable": "items",
"carIdVariable": "id"
}
And use these settings in my API client something like this:
foreach (var car in jObject.itemsArrayVariable)
{
string carId = car.carIdVariable.ToString();
}
Deserialize into a Dictionary<string, JToken> and dive into your data recursively. How to do so is documented in other Q&As.
But you shouldn't do this, it will make your code unmaintainable. API developers should not make such breaking changes, and you shouldn't prepare your code for in case they do this anyway. It's what API versioning is for.
This solution also isn't going to work if they're going to move the data to a deeper or higher level in the JSON, and so on. You would have to recompile anyway, and they should do a change like that on another endpoint anyway, allowing old clients to keep working.

Angular Material AutoComplete Not Loading Options MVC

I am working on my AutoComplete widget, using Angular JS - Material, in C# ASP.NET MVC.
In this example, I'm trying to get an AutoComplete of States to work. I started with this tutorial, but need to have the options come from the database (at this point, just static list of options in the controller).
Here is a link to my current code (relevant files).
When I run the code, I can see that the list of objects from the controller are pulling through properly - an array of 4 objects, each with an Id and a Name (as shown in my controller method). However, the options are not being loaded into the input form.
When I click into the textbox, I get an "option" that says No states matching "" were found. If I type in 'a', I get the "option" that says No states matching "a" were found. This is what I would expect to happen if there actually were no matching results.
My question: How do I change my code to load the options with what I'm pulling from the controller?
Thanks in advance!
Here's a screen shot of the results:
Here's the key JavaScript/AngularJS code:
//TODO: Replace data.json with /Home/GetStates
$http.get('data.json')
.then(function(response) {
self.states = response.data.map(function(state) {
return {
value: state.Name.toLowerCase(),
display: state.Name
};
});
});
You'll need to replace 'data.json' with '/Home/GetStates' for it to call your backend MVC JSON RESTful API Service. Note that I also left the functions querySearch(query) and createFilterFor(query) in place and changed self.states=loadStates() to self.states=null since the $http.get() service will now load the states.
I did test your C# MVC controller code and everything looks good with it.
Here's a working Plunker, http://plnkr.co/edit/tDC6KcO1O8VSGwVghBo7?p=preview.
Hope this helps. Let me know if you need anything else.
Here's what I did to fix the problem:
Changed to POST
Set variable = result, then returned the results
Here's the code:
function querySearch(query) {
if (query == undefined) {
console.log("invalid");
return;
}
$http({
method: 'POST',
url: self.URL_Get + query,
searchTerm: query
}).then(function(response) {
self.states = response.data;
});
return self.states;
}

jquery+jstree in .net - webservice responseformat?

I have been playing with jstree (1.0rc2)+jquery (1.4.2) for the first time with c#.net and although I have gotten it working, there are a couple things that I don't understand about how data is provided to the tree by the webservice I use to populate the tree (using ajax and the json_data plug-in). I was hoping someone with more experience using jstree could provide some insight.
The jstree config looks like this:
"json_data": {
"ajax": {
"url": "GetTree.asmx/GetChildren",
"type": "POST",
"contentType": "application/json; charset=utf-8",
"dataType": "json",
"data": function(n) {
var result = "{'id':'" + (n.attr ? n.attr("id").replace("node_", "") : "0") + "'}";
return (result);
}
}
}
GetTree.asmx GetChildren method:
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Xml )]
public string GetChildren(string id)
{
List<jsTreeNode> jsTree = new List<jsTreeNode>();
//... (build the tree as needed)
JavaScriptSerializer serializer = new JavaScriptSerializer();
return(serializer.Serialize(jsTree));
}
Question 1: So everything works great, so what's the problem? The problem is "ResponseFormat = ResponseFormat.Xml". I struggled for a while to get this working because it did not work when it was set to ResponseFormat.Json, which is what I would expect it to be. In that situation, no errors would be reported by the web service or by jQuery when parsing the json response, but the tree would render empty.
In looking at the HTML output of the web service, I could see no difference between what was rendered either way. I was hoping someone could explain why this works (counterintuitively) and why it does not work with ResponseFormat.Json, and if this is indicative of something else I might be doing wrong.
Question 2: Generally, web service or web handler?
Would using a generic web handler (ashx) be a more efficient way to do this anyway? Is there a substantial difference in the overhead required of a standard web service versus a generic web handler? Since my goal is basically to control exactly what is output (and using the json data format in the web service doesn't seem to be working the way I want it to anyway) I am not sure what benefit, if any, there is to using a web service here instead of just stripping it down completely. On the other hand this works now so maybe I should leave well enough alone.
Seeing as this question has almost 600 views and no answers I thought I would answer it myself (since I've long since figured it out).
Using a ScriptMethod is really not the right way to communicate with jQuery ajax. While it can be done, you will notice what I was doing above is returning a string with data that I encoded myself to JSON using JavascriptSerializer.
However, using a ScriptMethod automatically incorporates serialization/deserialization designed to communicate with Microsoft's AJAX framework. Since serializing a pure string with no object wrapper will genererally result in the same string (whether I be returning XML or JSON format), it basically worked, but what was really happening internally is it was being serialized twice.
So what I should have done, at a minimum, was:
public List<jsTreeNode> GetChildren(string id)
that is, the return type should be the actual data type, not a string of serialized data.
However, this still wouldn't be exactly right, because Microsoft's methods wrap the return value in an object d. I could still extract that in Javascript to get the inner data. But if something like jsTree is expecting data in a predefined format this may not be workable.
The best solution is do not use WebServices, use generic handlers (ashx) instead. This gives you complete control over the format and handling of your input and output. It may take a little bit of doing to set yourself up a nice framework, but the frustration of being unable to skip parts of the WebService handling that you don't need makes it well worth it.
Sorry, I have to disagree with your answer the 3.5 framework has really good support for Json serialization and Web Services (and for 2.0 you can use Newtonsoft.Json). Please see my JsTree ASP.NET Web Control Demo at http://code.zyky.com/jsTreeView/Default.aspx and http://asp-net-elephant.blogspot.com/2012/01/how-to-use-jstree-in-aspnet-web-forms.html for an example of both. Hope this helps.
Regarding Question 1 (I cannot speak to Q2), I have gotten a web service to feed JSON back to the jsTree plugin. While I recognize that WCF and REST are more current approaches, and no doubt better in the long run, we still use asmx web services and they do get the job done. But it was not easy: I spent a while trying to get the exact syntax to work in JS so that jsTree would get its data object from an ASP.NET web service. As the OP alludes to in his solution, the problem isn't so much my JS and wiring of the plugin but was with the wonky JSON data that was returned my web service. It -looked- like JSON but it was a string representation of a simplified JSON object.
To fix it I had to deserialize and serialize my json string (I got a clue about this from https://stackoverflow.com/a/20080495/1129926) and the resulting object was consumed happily by jsTree. Here's my web service:
Imports System.Web.Script.Serialization
Public Function GetJsonData() As String
Dim jss As JavaScriptSerializer = New JavaScriptSerializer
' IMPORTANT: do not use single quotes ' in JSON string, use ""
Dim jsonData As String = "" & _
"{ ""text"": ""CONTACTS"", ""children"": [ { ""text"": ""CUSTOMER"", ""column"": ""CLASS"", ""children"": [ { ""text"": ""Excelo Communications"", ""column"": ""COMPANY"", ""children"": [{ ""text"": ""Fred Shorts"", ""column"": ""CONTACT"" }] } ] }, { ""text"": ""USER"", ""column"": ""CLASS"" } ] }"
Dim o As Object = Nothing
Try
' deserialize the JSON into an Object,
' shout out to: https://stackoverflow.com/a/20080495/1129926
o = jss.Deserialize(Of Object)(jsonData)
o = jss.Serialize(o)
Context.Response.Clear()
Context.Response.ContentType = "application/json; charset=utf-8"
Catch ex As Exception
// log something
End Try
Return o
End Function
On the client I initialized jsTree in script block as follows:
$(document).ready(function () {
var sURL = "../dataIO.asmx/GetJsonData";
var dataIn = "";
$.ajax({
async: true,
type: "POST",
url: sURL,
dataType: "json",
data: dataIn,
contentType: "application/json; charset=utf-8",
success: function (data) {
console.log("data obj:" + data);
createJSTrees(data);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
console.log(XMLHttpRequest.statusText + "(status=" + XMLHttpRequest.status + "): " + XMLHttpRequest.responseText);
}
});
});
function createJSTrees(jsonData) {
$("#jstree_dataNav").jstree({
"core": {
"data": jsonData
}
});
$("#jstree_dataNav").on("changed.jstree", function (e, data) {
console.log(data.selected);
});
}
<div id="jstree_dataNav"></div>
jsTree has a somewhat alternative syntax whereas you call the web service within the core.data section but I was unable to get that to work. Instead, I call my web service via ajax and then pass the JSON data object to a function that initializes the jsTree plugin and jsTree just used the object passed in within data:.

Categories

Resources