How to pass complex objects in SignalR with proper casing? - javascript

I have a complex class like this in my c#:
public class Channel
{
public int Id { get; set; }
public string ChannelName { get; set; }
public Dictionary<int, Question> Questions { get; set; }
public Dictionary<int, ChatMessage> ChatMessages { get; set; }
public Dictionary<int, User> Users { get; set; }
public bool IsAdmin { get; set; }
public int TimeLeft { get; set; }
}
To pass it to my client i do:
Clients.Caller.CheckVersion(ChannelInstance);
My problem is that when i recieve the object on my client it will still have CamelCasing, instead of camelCasing. Is there any way to do this, so SignalR will automatically convert my object into an object with proper variable casing?
I know it's a pretty petty something, but i find it pretty annoying to have a class defined like this in my javascript:
function Channel() {
this.Id;
this.ChannelName;
this.etc;
}
when this looks much more JavaScript correct:
function Channel() {
this.id;
this.channelName;
this.etc;
}
Is there any way to do this, or will I just have to make do with the odd CamelCasing?

As Rob Segerink states in this answer, it's apparently not possible to change the global JsonSerializerSettings without breaking SignalR. A quick search of the source reveals that it sometimes does new JsonSerializer() and sometimes JsonSerializer.CreateDefault(), which might be causing at least part of the problem.
That being said, you may be able to adopt the trick from the question SignalR Typenamehandling to your needs, in particular to override Json.NET's behavior and use camel casing only for types marked with a specific attribute, or in assemblies marked with a specific attribute, using the following contract resolver:
public sealed class ConditionalCamelCaseContractResolver : IContractResolver
{
readonly static IContractResolver defaultContractResolver;
readonly static IContractResolver camelCaseContractResolver;
readonly static ConcurrentDictionary<Type, bool> camelCaseTable;
static Func<Type, bool> isCamelCase;
// Use a static constructor for lazy initialization.
static ConditionalCamelCaseContractResolver()
{
defaultContractResolver = new JsonSerializer().ContractResolver; // This seems to be the only way to access the global singleton default contract resolver.
camelCaseContractResolver = new CamelCasePropertyNamesContractResolver();
camelCaseTable = new ConcurrentDictionary<Type, bool>();
isCamelCase = (t) => GetIsCamelCase(t);
}
static bool GetIsCamelCase(Type objectType)
{
if (objectType.Assembly.GetCustomAttributes<JsonCamelCaseAttribute>().Any())
return true;
if (objectType.GetCustomAttributes<JsonCamelCaseAttribute>(true).Any())
return true;
foreach (var type in objectType.GetInterfaces())
if (type.GetCustomAttributes<JsonCamelCaseAttribute>(true).Any())
return true;
return false;
}
static bool IsCamelCase(Type objectType)
{
var code = Type.GetTypeCode(objectType);
if (code != TypeCode.Object && code != TypeCode.Empty)
return false;
return camelCaseTable.GetOrAdd(objectType, isCamelCase);
}
#region IContractResolver Members
public JsonContract ResolveContract(Type type)
{
return IsCamelCase(type) ? camelCaseContractResolver.ResolveContract(type) : defaultContractResolver.ResolveContract(type);
}
#endregion
}
[System.AttributeUsage(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Interface)]
public class JsonCamelCaseAttribute : System.Attribute
{
public JsonCamelCaseAttribute()
{
}
}
Next, mark your assemblies, types or interfaces with this attribute to enable camel casing:
[assembly: MyNamespace.JsonCamelCaseAttribute]
Finally, install the contract resolver with the techniques shown in that question using the following settings:
public static class ConverterSettings
{
public static JsonSerializer GetSerializer()
{
return JsonSerializer.Create(new JsonSerializerSettings()
{
ContractResolver = new ConditionalCamelCaseContractResolver()
});
}
}
Since SignalR's own internal types will not be so marked, they will continue to be serialized using default settings.
Note - tested with various test cases but not SignalR itself since I don't currently have it installed.

No, you can't, when you change the default JSON.net serialize settings on the server, by using the JsonSerializerSettings class, the SignalR jquery client will stop working because it expects it's server messages to be serialized by using the default JSON.net serialize settings. I believe in version 3 they will change this.

I know this is an old question but this quick solution might help someone coming across this problem. It certainly has helped me in the past.
The DataContract and DataMember attributes might be exactly what your a looking for to serialize your class in the way you want and still keep it upper case letter in C#.
Your class would look like this:
[DataContract]
public class Channel
{
[DataMember(Name = "id")]
public int Id { get; set; }
[DataMember(Name = "channelName")]
public string ChannelName { get; set; }
[DataMember(Name = "questions")]
public Dictionary<int, Question> Questions { get; set; }
...
}
This will serialize it just the way you want it.

Related

Is it possible to have a method in C# that implicitly deserializes an argument if it's passed as a JSON string?

Question
I have a handful of ViewComponents that look like so:
public IViewComponentResult Invoke(BuyerViewModel buyer)
I'd like them to be able to accept either a BuyerViewModel, or a JSON string representing a BuyerViewModel. For example, when you pass JSON to a controller method from JavaScript, if that method expects an argument of type Dog, the controller automatically attempts to deserialize the JSON to an instance of Dog. I'm trying to mimic that behavior.
The goal would be that both of these examples work:
var buyer = new BuyerSummaryViewModel() { FirstName = "John" };
ViewComponent("Buyer", buyer);
ViewComponent("Buyer", "{\"Name\":\"John Smith\"}");
Why?
I'm trying to make a generic JavaScript method that can fetch a ViewComponent on the fly:
const fetchViewComponent = async (viewComponentName, viewModel) => {
let data = { viewComponentName, viewModel };
let html = await $.get(`/Order/FetchViewComponent`, data);
return html;
}
//Get a BuyerViewComponent (example)
(async () => {
let component = await fetchViewComponent("Buyer", `#Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.Buyer))`);
console.log(component);
})();
What I've Tried
If I specify that the ViewModel is a BuyerViewModel, it works. The JSON string is automatically deserialized into a BuyerViewModel.
public class FetchViewComponentRequest
{
public string ViewComponentName { get; set; }
public BuyerViewModel ViewModel { get; set; }
// ^^^^^^^^^^^^^^
}
[HttpGet]
public IActionResult FetchViewComponent(FetchViewComponentRequest request)
{
return ViewComponent(request.ViewComponentName, request.ViewModel);
}
The Issue
However, I don't want to specify the type; I want this to be generic. So I tried this:
public class FetchViewComponentRequest
{
public string ViewComponentName { get; set; }
public string ViewModel { get; set; }
// ^^^^^^
}
[HttpGet]
public IActionResult FetchViewComponent(FetchViewComponentRequest request)
{
return ViewComponent(request.ViewComponentName, request.ViewModel);
}
But as expected, request.ViewModel isn't the correct type; it ends up null in the Invoke method. I was hoping there was a flag or something more global I could specify so that it tries to implicitly deserialize this string into the expected type.
Is there an easier way to do this that I haven't considered? Or, if not, is the way I'm envisioning even possible?
(I'm using .NET Core 2.2)
Maybe make your FetchViewComponentRequest generic?
public class FetchViewComponentRequest<T>
{
public string ViewComponentName { get; set; }
public T ViewModel { get; set; }
// ^^^^^^^^^^^^^^
}
[HttpGet]
public IActionResult FetchViewComponent(FetchViewComponentRequest<BuyerViewModel> request)
{
return ViewComponent(request.ViewComponentName, request.ViewModel);
}
The method needs to have some knowledge of what type to make the object coming in.
public T Convert<T>(dynamic obj) where T:class,new()
{
T myob = null;
if (obj !=null && obj is T)
{
myob = obj as T;
}
else if (obj is string)
{
//convert to type
myob = JsonConvert.DeserializeObject<T>(obj);
}
return myob;
}
Ok, im not sure about what you need.
But here is a dynamic way to do it, without specifying <T>.
//Assume that the namespace is DynamicTypeDemo
public class DynamicType {
// eg "DynamicTypeDemo.Cat, DynamicTypeDemo"
public string TypeName { get; set; } // the full path to the type
public string JsonString { get; set; }
}
Now you could simple DeserializeObject
public object ToObject(DynamicType dynamicType){
var type = Type.GetType(dynamicType.TypeName);
// Here you could check if the json is list, its really upp to you
// but as an example, i will still add it
if (dynamicType.JsonString.StartsWith("[")) // its a list
type =(List<>).MakeGenericType(type);
return JsonConvert.DeserializeObject(dynamicType.JsonString, type);
}
And here is how it work
var item = new DynamicType(){
TypeName = "DynamicTypeDemo.Cat, DynamicTypeDemo", // or typeof(Cat).AssemblyQualifiedName
JsonString = "{CatName:'Test'}"; // And for a list "[{CatName:'Test'}]"
}
object dynamicObject= ToObject(item); // return it to the javascript
Cat cat = dynamicObject as Cat; // Cast it if you want

Why is axios response in camel case when sending a request in asp.net core [duplicate]

I'm running through a WintellectNOW course on ASP.NET Core/Web API/Angular 2. I have the API portion implemented, but for whatever reason, the JSON that is being returned has the variable names being lowercased.
The returned JSON is formatted like...
[
{"id":1,"name":"Bowler","color":"black","count":1},
{"id":2,"name":"Fedora","color":"red","count":1},
{"id":3,"name":"Baseball Cap","color":"blue","count":3}
]
I'm expecting...
[
{"Id":1,"Name":"Bowler","Color":"black","Count":1},
{"Id":2,"Name":"Fedora","Color":"red","Count":1},
{"Id":3,"Name":"Baseball Cap","Color":"blue","Count":3}
]
Based on the C# model of...
namespace HatCollection.Models
{
public class Hat
{
public int Id { get; set; }
public string Name { get; set; }
public string Color { get; set; }
public int Count { get; set; }
}
}
I even went as far as decorating the properties with [DataMember(Name = "Id")] just to make sure and it still didn't matter.
On the off chance, it's relevant the Action and instance variable in the controller...
private static readonly List<Hat> MyHats = new List<Hat>
{
new Hat {Id = 1, Name = "Bowler", Color = "black", Count = 1 },
new Hat {Id = 2, Name = "Fedora", Color = "red", Count = 1 },
new Hat {Id = 3, Name = "Baseball Cap", Color = "blue", Count = 3 }
};
[HttpGet]
public IEnumerable<Hat> Get()
{
return MyHats;
}
How do I turn off the camelCase functionality, so that ASP.NET Core returns the property names without changing them?
In Asp.Net Core 3.0 some things have changed. For camelCase do nothing that is out of the box. For PascalCase or another set style use.
services.AddMvc(setupAction=> {
setupAction.EnableEndpointRouting = false;
}).AddJsonOptions(jsonOptions =>
{
jsonOptions.JsonSerializerOptions.PropertyNamingPolicy = null;
})
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
In Startup.cs ConfigureServices section
For those who needs a solution about a PascalCase within Api Project that has not the Mvc services you should add this after AddControllers services
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddJsonOptions(jsonOptions =>
{
jsonOptions.JsonSerializerOptions.PropertyNamingPolicy = null;
} ;
}
For Asp.Net Core 3.1 using the NewtonSoft.Json
services.AddControllers()
.AddNewtonsoftJson(options =>
{
options.UseMemberCasing();
});
In ASP.NET Core <3.0, JSON properties are camelCased by default (per this announcement).
You can disable this by replacing
services.AddMvc();
with
services
.AddMvc()
.AddJsonOptions(opt => opt.SerializerSettings.ContractResolver
= new DefaultContractResolver());
in your Startup.cs file. You'll have to add using Newtonsoft.Json.Serialization; to the top of the file.
With the DefaultContractResolver in place, the property names will be represented verbatim in the JSON output. No need for DataMember attributes.
Here is the answer for .net 5 :
https://learn.microsoft.com/en-us/aspnet/core/web-api/advanced/formatting?view=aspnetcore-5.0
Configure System.Text.Json based formatters Features for the
System.Text.Json based formatters can be configured using
Microsoft.AspNetCore.Mvc.JsonOptions.JsonSerializerOptions.
The
default formatting is camelCase. The following highlighted code sets
PascalCase formatting:
C#
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddJsonOptions(options =>
options.JsonSerializerOptions.PropertyNamingPolicy = null);
}
Another solution in Asp.Net.Core 2.2 as following:
services.AddMvc()
.AddJsonOptions(jsonOptions => jsonOptions.UseMemberCasing())
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
In ASP.Net Core you can use two way:
First way: UseMemberCasing()
In StartUp.cs :
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews().AddNewtonsoftJson(opt =>
{
opt.UseMemberCasing(); // <-- add this
});
}
Second way: ContractResolver
In StartUp.cs :
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews().AddNewtonsoftJson(opt =>
{
opt.SerializerSettings.ContractResolver = new DefaultContractResolver(); // <-- add this
});
}
depends on your project maybe you used AddMvc() or AddControllers() insted of AddControllersWithViews().
If AddNewtonsoftJson not found, you should install Nuget pacage : Microsoft.AspNetCore.Mvc.NewtonsoftJson (link).
You have to change the DefaultContractResolver which uses camelCase by default. Just set the NamingStatergy as null.
This should be done in the StartUp.ConfirgureService as follows.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddMvcOptions(o => o.OutputFormatters.Add(
new XmlDataContractSerializerOutputFormatter()));
.AddJsonOptions(o => {
if (o.SerializerSettings.ContractResolver != null)
{
var castedResolver = o.SerializerSettings.ContractResolver
as DefaultContractResolver;
castedResolver.NamingStrategy = null;
}
});
}
Option 2
Use JSonProperty as follows.
public class Hat
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("color")]
public string Color { get; set; }
[JsonProperty("count")]
public int Count { get; set; }
}
I am using the following solution because
a) I prefer using the .Net Core built in System.Text.Json serializer and
b) I do not want to rely on the not documented internal behaviour of
jsonOptions.JsonSerializerOptions.PropertyNamingPolicy = null;.
.
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = new MyTransparentJsonNamingPolicy();
});
where:
public class MyTransparentJsonNamingPolicy : JsonNamingPolicy
{
// You can came up any custom transformation here, so instead just transparently
// pass through the original C# class property name, it is possible to explicit
// convert to PascalCase, etc:
public override string ConvertName(string name)
{
return name;
}
}
In .NET 6 I used:
builder.Services.AddControllersWithViews().AddJsonOptions(opt => opt.JsonSerializerOptions.PropertyNamingPolicy = null);

How to pass dictionary with empty array as value

I have this class:
public class State{
public Dictionary<SecurityType, List<long>> assets { get; set; }
}
And action:
[HttpPost]
public virtual ActionResult GetHoldings(State state)
{
return Json(new HoldingsBL().GetHoldings(state));
}
public enum SecuritySerachType
{
Company = 1,
Security,
}
when i try to pass something like this:
{state:{assets :[{"Key":1,"Value":[]}]}}
i got empty dictionary in asset property.
i already read this solution , but i don`t realize how to solve my problem.
Some easy solution?
edit:
I try to add ValueProviderFactory as Oleksii Aza said, but there is a problem to compare it to backingStore(there is already exist a dictionary check, probably for nested objects):
var d = value as IDictionary<string, object>;
if (d != null)
{
foreach (var entry in d)
{
AddToBackingStore(backingStore, MakePropertyKey(prefix, entry.Key), entry.Value);
}
return;
}
So i still stuck with this issue.
Try using object-like JSON structure for C# Dictionary, for example:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
public class Program
{
public enum SecuritySerachType
{
Company = 1,
Security,
}
public class State{
public Dictionary<SecuritySerachType, List<long>> assets { get; set; }
}
public static void Main()
{
var state = JsonConvert.DeserializeObject<State>(#"{assets :{""1"":[]}}");
Console.WriteLine(state.assets[SecuritySerachType.Company].Count);
}
}
Link to dotnet fiddle

javascript call MVC Controller parameter always null when object field is private

I use javascript to call controller to process a function.
The parameter value (name in summary) is null when the field is set to private.
It works when the field is set to public.
Is set to public the only way? or there is a better way to do it?
thanks in advance.
My object
[DataContract]
public class Summary
{
[DataMember]
public int id { private set; get; }
[DataMember]
public string name { private set; get; }
public summary() {}
public summary(int id, string name)
{
id = id;
name = name;
}
}
MVC Controller
public ActionResult SetSummary(Summary summary)
{
string anme = summary.name; **<-- null if private**
...
}
Javascript
$http.post("MyController/SetSummary", JSON.stringify({
summary: mySummaryObject}))
.success(function (data, status, headers, config) {
....
}
yup, it should be public because defaultmodelbinder Maps a browser request to a data object. This class provides a concrete implementation of a model binder.
The DefaultModelBinder class maps the following types of objects to a browser request:
Primitive types, such as String , Double, Decimal , or DateTime objects.
Model classes, such as Person, Address, or Product.
Collections, such as ICollection, IList, or IDictionary.
Source: https://msdn.microsoft.com/en-us/library/system.web.mvc.defaultmodelbinder(v=vs.118).aspx
Hope it was useful kindly let me know your thoughts or feedbacks
Thanks
Karthik

How to consume a complex type returned by a WebMethod on client side using Javascript?

Iam a beginner with Webmethods and Services an I have landed in a situation here.
I have a webmethod that returns an instance of StatusViewModel class..
[WebMethod()]
public static StatusViewModel GetTime1()
{
Debug.Write("Timer");
StatusViewModel statusViewModel = new StatusViewModel();
statusViewModel.Label1 = "aaa";
statusViewModel.Label1 = "bbb";
statusViewModel.ListBox.Add("1", "ccc");
statusViewModel.ListBox.Add("2", "ccc");
return DateTime.Now.ToString();
}
StatusViewModel class looks like this one
public class StatusViewModel
{
public string Label1 { get; set; }
public string Label2 { get; set; }
public IDictionary<string, string> ListBox { get; set; }
}
How should I consume this method and set 2 labels and list-box with the data received from the method ?
Usually this is how I call webmethod
$(document).ready(function() {
call1();
});
function OnSuccess1(result, ctrl) { $('#<%=Label2.ClientID %>').html(result); }
function OnError1() { alert("error!"); }
function call1() { PageMethods.GetTime1(OnSuccess1, OnError1); }
This works if the return type is String.
Can any one suggest any simple articles regarding this topic ?
That one, could be nice for start.
http://encosia.com/using-jquery-to-consume-aspnet-json-web-services/
It gives you information, rest - just google and try something by your self. It is easy and very common task.
try converting your class to JSON. Then you can read it easily in javascript

Categories

Resources