Image->JSON->.Net Image - javascript

I am trying to learn Knockout and trying to create a photo uploader. I have successfully stored some images in an Array. Now I want to post back. In my knockout code (Javascript), I do this:
I have an image 'class' in Javascript:
// The image object that wukk be posted back.
function Image(_Image, _Description, _Filesize, _Filetype) {
var self = this;
self.Image =_Image;
self.Description = _Description;
self.Filesize = _Filesize;
self.Filetype = _Filetype;
self.DisplaySize = ko.computed(function () {
SizeToText(self.Filesize);
});
}
I have an array of these in a property called 'images'.
When I click the save button, I try to simply stringify the array of images into 'object'.
var object = JSON.stringify({
images: self.images(),
});
I then post my object back to my .net WebAPI controller.
var uri = "/api/Photo/Upload";
$.post({ url: uri, contentType: "application/json" }, object)
.done(function (data) {
self.images.removeAll();
});
My method gets called, and I can see in the data it receives, all the image data, descriptions etc.
The method is defined as:
public int Upload(UIImageUploadList data)
And a UIImageUploadList is a class that holds a list of images:
public class UIImageUploadList
{
public List<UIImageUpload> Images { get; set; }
}
and UIImageUpload is:
public class UIImageUpload
{
public string Image { get; set; }
public string Description { get; set; }
public string FileType { get; set; }
public int FileSize { get; set; }
}
So, I try and parse the data from the 'data' field, so that I can pass it to my service/logic/data layer.
foreach(var p in data.Images)
{
JavaScriptSerializer json_serializer = new JavaScriptSerializer();
var routes_list =
json_serializer.DeserializeObject(p.Image);
byte[] bytes = Convert.FromBase64String(p.Image);
Image image;
using (MemoryStream ms = new MemoryStream(bytes))
{
image = Image.FromStream(ms);
}
photos.Add(new Photo
{
Description = p.Description,
PhotoData = image
});
}
But it's in Base64, I think. (JSON.Stringify did that I believe).
The image property starts with this:
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJY
and ends with:
AElFTkSuQmCC"
I know if it's JPG or a BMP etc.
In .Net, I try to change this into an Image type, to pass to my DB.
But it fails "DeserializeObject" saying that 'data is not a primitive JSON type'.
How should I be converting to an image that will finally be stored in the database?

Before calling Convert.FromBase64String, you will need to split the string containing the encoded image data. First, strip of the data:, then you get the MIME type for the image (image/png), then the encoding (base64) and only the remainder of the string (after the comma) can be passed into the conversion function. I don't have much experience with C# but from there on the FromStream function should be smart enough to deal with the decoded data.

Related

Error deserializing JSON with Newtonsoft, C#

I'm receiving the following error:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'System.Collections.Generic.Dictionary`2[System.String,System.Collections.Generic.List`1[ReportingDataSchema.CurrentBusinessUnits]]'
because the type requires a JSON object (e.g. {\"name\":\"value\"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {\"name\":\"value\"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array.
JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array. Path 'selectedBusinessUnits', line 1, position 26."} System.Exception {Newtonsoft.Json.JsonSerializationException}
My JSON:
{
"selectedBusinessUnits": [{
"guidNode": "some value",
"businessUnit": "some value",
"fileName": "some value"
}, {
...
}]
}
I'm trying to first transform this response into the following:
public class EnAFileGenerator
{
private Dictionary<string, List<CurrentBusinessUnits>> selectedBusinessUnits;
public Dictionary<string, List<CurrentBusinessUnits>> SelectedBusinessUnits
{
get { return selectedBusinessUnits; }
set { selectedBusinessUnits = value; }
}
}
So that ultimately I can access the array in the JSON, using the following:
public class CurrentBusinessUnits
{
private string guidNode;
private string businessUnit;
private string fileName;
public string GuidNode { get; set; }
public string BusinessUnit { get; set; }
public string FileName { get; set; }
}
The code that's generating the error:
// JSON Data is the parameter from the client, containing the above JSON object
EnAFileGenerator resultArray = JsonConvert.DeserializeObject<EnAFileGenerator>(JSONData);
From what I've read, it seems like my error is a result of the parsing the array (the value for the property selectedBusinessUnits) into the desired C# collection.
After implementing #DavidG's suggestion, I'm still receiving the following:
Error converting value \"{\"guidNode\":\"some value\",\"businessUnit\":\"some value\",\"fileName\":\"some value.xlsx\"}\"
to type 'ReportingDataSchema.CurrentBusinessUnits'. Path 'selectedBusinessUnits[0]', line 1, position 159."}
System.Exception {Newtonsoft.Json.JsonSerializationException}
Prany's solution nearly got me there. I was able to modify that code to utilize the objects I already had:
var files = JObject.Parse(JSONData);
var recList = files.SelectToken("$..selectedBusinessUnits").ToList();
foreach (string item in recList)
{
JObject businessUnit = JObject.Parse(item);
CurrentBusinessUnits currentBusinessUnit = businessUnit.ToObject<CurrentBusinessUnits>();
}
The problem is you are trying to deserialise into the wrong type. you have specified a Dictionary<string, List<CurrentBusinessUnits>> but really you only need a List<CurrentBusinessUnits>:
public class EnAFileGenerator
{
public List<CurrentBusinessUnits> SelectedBusinessUnits { get; set; }
}
You can use Jobject since you're using Newtonsoft. For getting values based on selectedBusinessUnits. Use below
var files = JObject.Parse(YourJson);
var recList = files.SelectToken("$..selectedBusinessUnits").ToList();
foreach (JObject item in recList)
{
foreach (JProperty prop in item.Children())
{
string key = prop.Name.ToString();
string value = prop.Value.ToString();
}
}

POST 500 error when sending JSON stringified javascript object to controller

I'm currently working on a ASP.net Web application with the MVC pattern. I have set up a page where people can add items to a gridstack section and drag them around (Gridstack is a plugin to create draggable boxes/objects). My goal is to send the final coordinates of these boxes to my controller:
function saveData() {
var pagePositions = [];
// Fill our array
$('.grid-stack-item.ui-draggable').each(function () {
var $this = $(this);
pagePositions.push({
x: $this.attr('data-gs-x'),
y: $this.attr('data-gs-y'),
w: $this.attr('data-gs-width'),
h: $this.attr('data-gs-height'),
content: $('.grid-stack-item-content', $this).html()
});
});
// Convert array to object
var pagePosData = toObject(pagePositions);
alert(pagePosData);
$.ajax({
type: "POST",
url: "savePage",
data: { positions: JSON.stringify(pagePosData) }
});
} function toObject(arr) {
var rv = {};
for (var i = 0; i < arr.length; ++i)
if (arr[i] !== undefined) rv[i] = arr[i];
return rv;
}
The code above fills the attributes of the given html elements and puts them into an array. I assumed, according to several topics on the internet, that sending the array was causing trouble so i inserted a function that converts the array to an javascript object (key value pairs). I send them to my controller with an AJAX call which results in a error code 500:
[HttpPost]
public string savePage(string positions)
{
//some code
var json = positions;
var test = "";
CreatePagemodelJson obj = new JavaScriptSerializer().Deserialize<CreatePagemodelJson>(positions.ToString());
//var jsonObject = JsonConvert.DeserializeObject<CreatePagemodel>(positionsJson.ToString());
return "";
}
I set up breakpoints inside the controller to read the value i get from the parameter positions, but it doesn't even get to that point. I tried setting up models for the Json file but the problem here is that the post calls return a dynamic json format.
Update:
I managed to get it working with below posts. With below structure, i get the results as an array according to my model.
function saveData() {
var pagePositions = [];
// Fill our array
$('.grid-stack-item.ui-draggable').each(function () {
var $this = $(this);
pagePositions.push({
x: $this.attr('data-gs-x'),
y: $this.attr('data-gs-y'),
w: $this.attr('data-gs-width'),
h: $this.attr('data-gs-height'),
content: $('.grid-stack-item-content', $this).html()
});
});
alert(pagePositions);
$.ajax({
type: "POST",
url: "savePage",
contentType: 'application/json',
data: JSON.stringify(pagePositions)
});
}
public class Position
{
public string x { get; set; }
public string y { get; set; }
public string w { get; set; }
public string h { get; set; }
public string content { get; set; }
}
[HttpPost]
public ActionResult savePage(IEnumerable<Position> positions)
{
//some code
return View("SomeView");
}
The parameter positions succesfully returns the array of pagePositions send with the post:
I tried sending the data without the JSON.stringify(pagePositions) and ContentType: 'application/json' option but i got a null return in my parameter on the controller.
There a number of things to point out here.
With your current question.
The data you are sending isn't a string. data: { positions: JSON.stringify(pagePosData) } is creating a JavaScript object with a property of positions whose value is the serialized version of your object.
You should be able to create a c# class and accept that as your controller methods parameter and get closer to the results you are expecting. That would look as follows:
public class Foo
{
public string Positions { get; set; }
}
public string savePage(Foo positions)
Alternatively you could update your method to get the request body as a string as shown in this SO answer
More importantly though, it would seem as though you came across some misinformation. There shouldn't be any issue passing an array back to your controller. Again, you will need a c# class that models your expected data type(s). You don't list those in your question, so it is hard to say for sure how you need to update your code, but I would suggest something more like the following:
function saveData() {
var pagePositions = [];
// Fill our array
$('.grid-stack-item.ui-draggable').each(function () {
var $this = $(this);
pagePositions.push({
x: $this.attr('data-gs-x'),
y: $this.attr('data-gs-y'),
w: $this.attr('data-gs-width'),
h: $this.attr('data-gs-height'),
content: $('.grid-stack-item-content', $this).html()
});
});
$.ajax({
type: "POST",
url: "savePage",
data: JSON.stringify(pagePositions),
contentType: "application/json"
});
}
public class Position
{
public int X { get; set; }
public int Y { get; set; }
public int W { get; set; }
public int H { get; set; }
public string Content { get; set; }
}
[HttpPost]
public string savePage(Position[] positions)
{
//some code
return "";
}
If this doesn't work, please provide more details about what doesn't work, what error you are receiving and what version of asp.net mvc you are using.

Kendo UI hidecolumn/showcolumn performance with many columns

I am trying to hide and show columns in my grid using Kendo UI hidecolumn and showcolumn methods, it works however it looks like the grid lags in showing and hiding column when there are many columns in grid. I have about 50 columns in my grid. Also after hiding the columns, the column doesn't realign in the grid even after using refresh method of the grid.
Does anyone know what's an alternative way to hide/show column without loosing the performance in the grid?
Thanks.
Sanjeev
Well, first of all, you just need to know, that columns in grid is nothing more than usual array. So you can create this array separately and assign into grid configuration.
I done it for configuration of whole grid but I try show only columns part, because example will be relative long because I will try show all important methods. Anyway I hope it helps you.
So process can be (but don't have to) be something like this:
Prepare database table. If you need save only visible/hid value, you can be satisfied with basic table like this:
CREATE TABLE dbo.TableSettings(
Id INT PRIMARY KEY IDENTITY,
[Key] VARCHAR(MAX) NOT NULL,
Name VARCHAR(MAX) NULL,
Value BIT NULL );
In my case table has more columns but for save this information this is enough. [Key] is table name in my case (dbo.[User] for example), Name is name of column (FirstName for example) and Value is Visible/hid (1/0).
Now you need to load columns settings from database and save it into array witch will be assigned into grid config.
RunSynchronousAjax(address, null, function (e) {
var options = JSON.parse(e.Config);
var columns = options.Columns;
//This part is needed only if you have some special cases which are prepared on server. It is because on server you cannot assign function like in javascript but you have to assign only name of function which need to be eval in javascript.
for (var i = 0; i < options.columns.length; i++) {
options.columns[i].filterable.ui = eval(options.columns[i].filterable.ui);
if (options.columns[i].filterable.itemTemplate != undefined) {
options.columns[i].filterable.itemTemplate = eval(options.columns[i].filterable.itemTemplate);
}
}
}, defaultErrorCallBack);
Note: RunSynchronousAjax is my helper method for processign ajax request because I didn't want write it everztime I need it. It looks like this:
function RunSynchronousAjax(url, data, successCallback, errorCallback) {
jQuery.ajax({
contentType: 'application/json; charset=utf-8',
url: url,
data: data,
type: "POST",
cache: false,
success: function (json) {
successCallback(json);
},
error: function (data) {
errorCallback(data);
},
async: false
});
}
function defaultErrorCallback(data) {
alert("Unexpected error. Please, try press CTRL+F5.");
}
This function is way how I load config for table, now let's look on method in controller.
public JsonResult GetSetting()
{
KendoGrid grid = new KendoGrid();
grid.PrepareColumns();
string json = JsonConvert.SerializeObject(grid);
return Json(new
{
Config = json
});
}
Objects looks like this
public class KendoGrid
{
...
private List<Column> _Columns = new List<Column>();
[JsonProperty("columns")]
public List<Column> Columns
{
get { return this._Columns; }
set { this._Columns = value; }
}
...
}
public class Column
{
[JsonProperty("field")]
public string Field { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("width")]
public string Width { get; set; }
[JsonProperty("filterable")]
public object Filterable { get; set; }
[JsonProperty("template")]
public string Template { get; set; }
[JsonProperty("order")]
public int Order { get; set; }
[JsonProperty("format")]
public string Format { get; set; }
.... and more ... just what you need
public Column(
string field,
string title = null,
string type = "string",
string width = null,
string template = null,
object filterable = null,
string format = null,
...)
{
this.Field = field;
this.Title = title ?? field;
this.Type = type;
this.Width = width;
this.Template = template;
this.Order = order;
this.Format = format;
....
}
In point 3 there is method PrepareColumns, it looks like this
public void PrepareColumns()
{
var listOfColumns = TableSettings.GetColumns("dbo.TableName");
this.Columns.AddNotNull(Column.GetColumn<ObjectWhichIsDisplayedInGrid>(x => x.Id, listOfColumns,
type: "number",
width: "140px",
...));
this.Columns.AddNotNull(Column.GetColumn<ObjectWhichIsDisplayedInGrid>(x => x.Name, listOfColumns,
width: "250px",
...));
.....
}
//This method is based on you, It just load records from db...
public static List<TableSettings> GetColumns(string key)
{
List<TableSettings> result = new List<TableSettings>();
var query = Sql.Builder
.Select("*")
.From("dbo.TableSettings")
.Where("[Key] = #0", key)
.OrderBy("OrderInTable");
using (IDatabase db = DbConnection.Connect())
{
result = db.Fetch<TableSettings>(query);
}
return result;
}
public static Column GetColumn<T>(
Expression<Func<T, object>> expression,
List<TableSettings> settings,
string type = "string",
string width = null,
string template = null,
...)
{
var fieldName = PropertyNameHelper.PropertyName<T>(expression);
var fieldDescription = PropertyNameHelper.PropertyDescription<T>(expression);
var column = settings.Where(c => c.Name.Replace(" ", string.Empty).ToLower() == fieldName.Replace(" ", string.Empty).ToLower()).FirstOrDefault();
Column col = new Column(
field: fieldName,
title: fieldDescription,
type: type,
....);
return col;
}
public static string PropertyName<T>(Expression<Func<T, object>> expression)
{
return GetPropertyName(expression);
}
public static string PropertyDescription<T>(Expression<Func<T, object>> expression)
{
var propertyName = GetPropertyName(expression);
PropertyInfo prop = typeof(T).GetProperty(propertyName);
if (prop.IsDefined(typeof(DisplayAttribute), false))
{
return (prop.GetCustomAttribute(typeof(DisplayAttribute), false) as DisplayAttribute).Name;
}
return propertyName;
}
That's it. First 5 points is about load columns configuration from database. What is saved as not visible, will not be loaded so kendo grid will know only about columns which are visible.
Now, I have talked about my own columnMenu. It is only usual popup window with options. Important is that grid has set columnMenu to false, so default functionality is off. I have added button into toolbar and click event has opened kendoWindow where I had checkboxes with columns names. I can check what I want to display and after popup is closed/confirmed, some ajax request is called - it is saved into database and grid is refreshed.
Anyway mainly it can gives you idea, I believe there is much more solutions for this. If you find way how to create columns array dynamically you will win.

Retrieving multiple values from C# to JavaScript

So I have an a tag with an onclick:
<a onclick="join(4);">Join</a>
Now when the a tag is clicked, it calls this code in this order:
JavaScript function:
function join(gymID) {
PageMethods.getGymInformation(gymID);
}
C# method:
[WebMethod]
public gymData getGymInformation(string gymID)
{
gyms gym = new gyms();
DataTable dt = gym.getNewGymInfo(System.Convert.ToInt32(gymID));
DataRow dr = dt.Rows[0];
return new gymData { name = dr["name"].ToString(), strength = dr["strength"].ToString(), speed = dr["speed"].ToString(), defence = dr["defence"].ToString()};
}
public DataTable getNewGymInfo(int gymID)
{
// This returns a datatable with 1 row
return gymTableApapter.getNewGymInfo(gymID);
}
[Serializable]
public sealed class gymData
{
public string name { get; set; }
public string strength { get; set; }
public string speed { get; set; }
public string defence { get; set; }
}
As you can see, the JavaScript join function calls the C# method which then retrieves a DataTable with 1 row, then using a custom data type it populates the strings with data to be returned..
Now I'm trying to figure out how to get the information returned from the C# method to be extracted in the JavaScript join function?
Is their a way to do this?
Add success/error callbacks in your JavaScript code. It might look something like this:
PageMethods.getGymInformation(gymID, onSuccess, onError);
function onSuccess (result) {
// result contains the returned value of the method
}
function onError (result) {
// result contains information about the error
}
Then in the onSuccess function you would have the returned value of the server-side method (of type gymData) in the result variable. At that point you can do whatever you need to do with it in your client-side code.
If the functions don't have any applicable re-use, you can even just add them in-line:
PageMethods.getGymInformation(gymID,
function (result) {
// success
}, function (result) {
// error
});

ASP.NET MVC - How to "reverse" model binding to convert a C# model back to a query string representation

I have a custom javascript on the client side that I use to build up a querystring and pass over to my asp.net-mvc controller
var templateQueryString = BuildTemplate();
$.ajax({
url: '/MyController/Save?' + templateQueryString,
type: 'post',
dataType: 'json',
success: function (data) {
}
}
and on my controller all of the properties leverage the model binding so it comes in as a single object on the server side. NOTE: that this is a pretty complex object with arrays and arrays of sub objects:
public ActionResult Save(MyTemplate template)
{
}
the issue now is that I need to be able to convert from my C# object back to a string that represents "myTemplateQueryString" on the client side.
Is there any recommended way to take an object and do the "reverse" model binding. They key here is that it generates a string that I could use as a query string again in the future to pass into another asp.ent-mvc controller action.
Here is an example of the querystring that I am storing locally:
<input type="hidden" value="showIds=false&showRisks=false&
amp;statusIds=2&statusIds=1&statusIds=6&statusIds=8&
amp;statusIds=3&statusIds=9&showCompleted=0"
name="filterQueryString" id="filterQueryString">
As #haim770 said it would be easier if you used JSON in the request payload, and not the query string to pass your complex object to the server.
Regarding creating the query string from a model there is not a built-in method that does something like that or any recommended approach as far as i know. An obvious solution is to use reflection and build the query string from your properties.
Assuming your BuildTemplate class looks something like:
public class BuildTemplate
{
public bool ShowIds { get; set; }
public bool ShowRisks { get; set; }
public bool ShowCompleted { get; set; }
public int[] StatusIds { get; set; }
}
You can develop an extension method to convert any object to a QueryString. Here is some initial code you can start with:
public static class ObjectExtensions
{
public static string ToQueryString(this Object obj)
{
var keyPairs = obj.GetType().GetProperties().Select(p =>
new KeyValuePair<string, object>(p.Name.ToLower(), p.GetValue(obj, null)));
var arr = new List<string>();
foreach (var item in keyPairs)
{
if (item.Value is IEnumerable && !(item.Value is String))
{
foreach (var arrayItem in (item.Value as IEnumerable))
{
arr.Add(String.Format("{0}={1}", item.Key, arrayItem.ToString().ToLower()));
}
}
else
arr.Add(String.Format("{0}={1}", item.Key, item.Value.ToString().ToLower()));
}
return "?" + String.Join("&", arr);
}
}
Then you can easily invoke this code on any object to generate a query string:
var person = new BuildTemplate() { StatusIds = new []{ 1, 5, 8, 9 }, ShowRisks = true };
var queryString = person.ToQueryString();
This would generate a query string like:
"?showids=false&showrisks=true&showcompleted=false&statusids=1&statusids=5&statusids=8&statusids=9"
This query string should work just fine with the default model binder for the BuildTemplate class.

Categories

Resources