JSON converter on azure mobile service queries - mobile

One of my tables in Azure Mobile has a column defined as Number. On the client side that column maps to an enum. I have a JSON converter that marshals the enum value to int (code below).
class CompletedStateConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(CompletedState);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return CompletedState.NotCompleted;
int intVal = serializer.Deserialize<int>(reader);
CompletedState gameCompleted = (CompletedState)intVal;
return gameCompleted;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
CompletedState gameCompleted = (CompletedState)value;
int number = (int)gameCompleted;
serializer.Serialize(writer, number);
}
}
All worked fine until I needed to make a change in one of the queries I use to read data from that table.
data = await App.GameTable
.Take(15)
.OrderByDescending(g => g.LastMoveTime)
.Where(g => g.Completed == CompletedState.NotCompleted)
.ToListAsync();
Notice the where clause that selects the items where the Completed state is NotCompleted (value of 0 in the enum list).
On the backend the query gets translated into string instead of using the number that should be provided by the JSON converter.
Here's the content of query.getComponents()
{
"filters":
{
"operator":"And",
"left":{"queryString":"(Completed eq 'NotCompleted')","args":[],"type":"LiteralExpression"},
"right":
{
"operator":"Or",
…removed
"type":"BinaryExpression"
},
"type":"BinaryExpression"
},
"selections":[],
"projection":null,
"ordering":{"LastMoveTime":false},
"skip":null,
"take":15,
"table":"Game",
"includeTotalCount":false,
"version":4
}
I expected that the query would leverage the JSON converter to convert the enum into the number. Why isn’t it doing so and what should I do to make it work?
Thank you

Related

"SearchAsync" InvalidCastException Null object cannot be converted to a value type

I use Azure search and a field in the search index can be null.
Which means my code
var result = await searchIndexClient.Documents.SearchAsync<SearchEntity>(query, parameters);
throws an exception
"InvalidCastException Null object cannot be converted to a value type."
for Csharp:
public class SearchEntity{
...
boolean NewField{ get; set; }
...
}
and index document:
{
"#odata.context": "...",
"value": [
{
"#search.score": 1,
...
"NewField": null,
...
I would like to tell SearchAsync(?) to set a default value to the property in SearchEntity if the received field is null.
Is it possible?
(I know I can receive a null and default it later but what is the fun in that?)
One possible solution would be to define the default values in your entity and tell the JSON serializer to ignore the null values from source at the time of serialization.
For example, consider the following entity definition:
public class SearchEntity
{
[JsonProperty(PropertyName = "newField")]
public bool NewField { get; set; } = true;
[JsonProperty(PropertyName = "dateTime")]
public DateTime DateTime { get; set; } = DateTime.Now.Date;
}
and here's the code for serialization. I am using NewtonSoft.Json for that and instructing it to ignore the null values:
string json = "{\"newField\": false, \"dateTime\": null}";
JsonSerializerSettings settings = new JsonSerializerSettings()
{
NullValueHandling = NullValueHandling.Ignore
};
var searchEntity = JsonConvert.DeserializeObject<SearchEntity>(json, settings);
Console.WriteLine($"SearchEntity.NewField: {searchEntity.NewField}");//Prints "true"
Console.WriteLine($"SearchEntity.DateTime: {searchEntity.DateTime.ToString("u")}");//Prints current date e.g. "2021-07-01 00:00:00Z"

MVC Custom Validation String Array Client Side

There are examples out there of custom MVC validators that take an array parameter, but only server side - none of them show an example of implementing the client side with array parameter.
The problem is instead of outputting the array's contents in the html data- attribute, it outputs "System.String[]":
data-val-total-propertynames="System.String[]"
Here is my attribute class:
public class TotalAttribute : ValidationAttribute, IClientValidatable
{
private String[] PropertyNames { get; set; }
public TotalAttribute(String[] propertyNames)
{
PropertyNames = propertyNames;
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
float total = 0;
foreach (var propertyName in PropertyNames)
total += (float)context.ObjectInstance.GetType().GetProperty(propertyName).GetValue(context.ObjectInstance, null);
if (total != (float)value)
return new ValidationResult(FormatErrorMessage(context.DisplayName), new[] { context.MemberName });
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = ErrorMessageString,
ValidationType = "total",
};
rule.ValidationParameters["propertynames"] = PropertyNames;
yield return rule;
}
}
Here it is implemented in the model:
[Total(new string[] { "SomeOtherField1", "SomeOtherField2" }, ErrorMessage = "'Line12Balance' must equal total of 'SomeOtherField1' and 'SomeOtherField2'")]
public decimal? Line12Balance { get; set; }
Here is the html data-val attribute output:
data-val-total-propertynames="System.String[]"
What am I doing wrong?
You get "System.String[]" string because the value of ValidationParameters["propertynames"] is written by calling ToString on it, so string[] variable returns "System.String[]" in this case. If you need to output specific value you need to format it by yourself in your validation attribute. For example, change
rule.ValidationParameters["propertynames"] = PropertyNames;
to
rule.ValidationParameters["propertynames"] = string.Join(",", PropertyNames);
and you will get
data-val-total-propertynames="SomeOtherField1,SomeOtherField2"
As Alexander mentioned in his answer, the issue you encountered is that string[] doesn't have its own ToString() implementation, and uses the base object.ToString() implementation, which just displays the type of the object rather than the contents of your array.
So you've got to somehow serialize your string array so that it can be stored in a string to then parse in your client-side JavaScript.
If you're already using JSON.Net in your solution (as many are), you can also do this by JSON serializing the array on the server side, then parsing the JSON client-side.
i.e. server-side:
rule.ValidationParameters["propertynames"] = JsonConvert.SerializeObject(PropertyNames);
Client-side: (
$.validator.addMethod('rule', function(value, element, params) {
var propertynames = JSON.parse(params.propertynames);
// do your validation
});
That prevents you from having to escape commas/delimiters in your parameters.

How to deserialize dodgy JSON (with improperly quoted strings, and missing brackets)?

I am having to parse (and ultimately reserialize) some dodgy JSON. it looks like this:
{
name: "xyz",
id: "29573f59-85fb-4d06-9905-01a3acb2cdbd",
status: "astatus",
color: colors["Open"]
},
{
name: "abc",
id: "29573f59-85fb-4d06-9905-01a3acb2cdbd",
status: "astatus",
color: colors["Open"]
}
There are a number of problems here - starting with the most severe.
color: colors["Open"]
WTF even is that? If I drop 'colors' then I can get an array of strings out but I can't tweak to work out of the box.
It is an array without square brackets. I can fix this by wrapping in them. But is there a way to support out of the box?
Properties have no quotes. Deserializing is fine for these.. but reserializing is just no dice.
Any suggestions of handling both in and out of this structure?
Answering your questions #1 - #3 in order:
Json.NET does not support reading dodgy property values in the form colors["Open"] (which, as you correctly note, violates the JSON standard).
Instead, you will need to manually fix these values, e.g. through some sort of Regex:
var regex = new Regex(#"(colors\[)(.*)(\])");
var fixedJsonString = regex.Replace(jsonString,
m => string.Format(#"""{0}{1}{2}""", m.Groups[1].Value, m.Groups[2].Value.Replace("\"", "\\\""), m.Groups[3].Value));
This changes the color property values into properly escaped JSON strings:
color: "colors[\"Open\"]"
Json.NET does, however, have the capability to write dodgy property values by calling JsonWriter.WriteRawValue() from within a custom JsonConverter.
Define the following converter:
public class RawStringConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override bool CanRead { get { return false; } }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var s = (string)value;
writer.WriteRawValue(s);
}
}
Then define your RootObject as follows:
public class RootObject
{
public string name { get; set; }
public string id { get; set; }
public string status { get; set; }
[JsonConverter(typeof(RawStringConverter))]
public string color { get; set; }
}
Then, when re-serialized, you will get the original dodgy values in your JSON.
Support for deserializing comma-delimited JSON without outer brackets will be in the next release of Json.NET after 10.0.3. see Issue 1396 and Issue 1355 for details. You will need to set JsonTextReader.SupportMultipleContent = true to make it work.
In the meantime, as a workaround, you could grab ChainedTextReader and public static TextReader Extensions.Concat(this TextReader first, TextReader second) from the answer to How to string multiple TextReaders together? by Rex M and surround your JSON with brackets [ and ].
Thus you would deserialize your JSON as follows:
List<RootObject> list;
using (var reader = new StringReader("[").Concat(new StringReader(fixedJsonString)).Concat(new StringReader("]")))
using (var jsonReader = new JsonTextReader(reader))
{
list = JsonSerializer.CreateDefault().Deserialize<List<RootObject>>(jsonReader);
}
(Or you could just manually surround your JSON string with [ and ], but I prefer solutions that don't involve copying possibly large strings.)
Re-serializing a root collection without outer braces is possible if you serialize each item individually using its own JsonTextWriter with CloseOutput = false. You can also manually write a , between each serialized item to the underlying TextWriter shared by every JsonTextWriter.
Serializing JSON property names without a surrounding quote character is possible if you set JsonTextWriter.QuoteName = false.
Thus, to re-serialize your List<RootObject> without quoted property names or outer braces, do:
var sb = new StringBuilder();
bool first = true;
using (var textWriter = new StringWriter(sb))
{
foreach (var item in list)
{
if (!first)
{
textWriter.WriteLine(",");
}
first = false;
using (var jsonWriter = new JsonTextWriter(textWriter) { QuoteName = false, Formatting = Formatting.Indented, CloseOutput = false })
{
JsonSerializer.CreateDefault().Serialize(jsonWriter, item);
}
}
}
var reserializedJson = sb.ToString();
Sample .Net fiddle showing all this in action.

Silverlight 4 Late bound operations cannot be performed... and no examples help?

I have tried all variaions that I can find others using, and frankly they all seem to boil down to what I already have.
I want a generic system for invoking methods based on generic inputs. Not sure that really captures it fully, but not sure how to state it.
Here we go:
I want to make breadcrumbs from a params of Expression>
here, SelectedDivisions is ObservableCollection and ModelId is long?.
So, the point is that I want to feed in a list of varying properties, have them processed by the data class such that each is processed by the appropriate method inside of data
data.MakeBreadCrumbs(() => dc.SelectedDivisions, () => dc.ModelId);
data contains the following code:
public void MakeBreadCrumbs(params Expression<Func<object>>[] propertyExpressions) {
foreach (Expression<Func<object>> propertyExpression in propertyExpressions) {
MemberExpression member = propertyExpression.Body as MemberExpression;
if (member == null) {
UnaryExpression uExp = propertyExpression.Body as UnaryExpression;
member = uExp.Operand as MemberExpression;
}
PropertyInfo propInfo = member.Member as PropertyInfo;
Type[] propTypes = propInfo.PropertyType.GetGenericArguments();/
MethodInfo methodInfo = typeof(BreadcrumbData).GetGenericMethod("MakeBreadCrumb", new Type[] { propInfo.PropertyType, typeof(string) }); //
if (methodInfo.IsGenericMethod) {
methodInfo.MakeGenericMethod(propTypes[0]);
}
ConstantExpression ce = Expression.Constant(propertyExpression.Compile().Invoke());
string criterionName = ReadCriterionName(propertyExpression);
methodInfo.Invoke(this, new object[] { ce.Value, criterionName });
}
the last line fails with "Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true." when I am processing the property expression for the ObservableCollection item.
Here are the methods defined in the data class which are available, and which are correctly selected, but the one for the ObservableCollection fails on invocation
(LookupTypeBase is a class particular to my solution, but insert any type here that works with the type of a fake ObservableCollection property)
public void MakeBreadCrumb<T>(ObservableCollection<T> selections, string criterionName) where T : LookupTypeBase {...}
public void MakeBreadCrumb(long? value, string criterionName) {...}
public static class xxx {
public static MethodInfo GetGenericMethod(this Type type, string name, Type[] parameterTypes) {
var methods = type.GetMethods();
foreach (var method in methods.Where(m => m.Name == name)) {
var methodParameterTypes = method.GetParameters().Select(p => p.ParameterType).ToArray();
if (methodParameterTypes.SequenceEqual(parameterTypes, new SimpleTypeComparer())) {
return method;
}
}
return null;
}
private class SimpleTypeComparer : IEqualityComparer<Type> {
public bool Equals(Type x, Type y) {
return x.Assembly == y.Assembly && x.Namespace == y.Namespace && x.Name == y.Name;
}
public int GetHashCode(Type obj) {
throw new NotImplementedException();
}
}
}
It's silverlight 4 so you have latebound method invocation with the dynamic keyword, which should make this easy and fast:
BreadcrumbData.MakeBreadCrumb((dynamic)ce.Value, (dynamic)criterionName);
Aside from that i think your issue is that MakeGenericMethod returns a new methodinfo which you ignore instead of keeping around like so:
if (methodInfo.IsGenericMethod) {
methodInfo = methodInfo.MakeGenericMethod(propTypes[0]);
}

How do I dynamically generate columns in a WPF DataGrid?

I am attempting to display the results of a query in a WPF datagrid. The ItemsSource type I am binding to is IEnumerable<dynamic>. As the fields returned are not determined until runtime I don't know the type of the data until the query is evaluated. Each "row" is returned as an ExpandoObject with dynamic properties representing the fields.
It was my hope that AutoGenerateColumns (like below) would be able to generate columns from an ExpandoObject like it does with a static type but it does not appear to.
<DataGrid AutoGenerateColumns="True" ItemsSource="{Binding Results}"/>
Is there anyway to do this declaratively or do I have to hook in imperatively with some C#?
EDIT
Ok this will get me the correct columns:
// ExpandoObject implements IDictionary<string,object>
IEnumerable<IDictionary<string, object>> rows = dataGrid1.ItemsSource.OfType<IDictionary<string, object>>();
IEnumerable<string> columns = rows.SelectMany(d => d.Keys).Distinct(StringComparer.OrdinalIgnoreCase);
foreach (string s in columns)
dataGrid1.Columns.Add(new DataGridTextColumn { Header = s });
So now just need to figure out how to bind the columns to the IDictionary values.
Ultimately I needed to do two things:
Generate the columns manually from the list of properties returned by the query
Set up a DataBinding object
After that the built-in data binding kicked in and worked fine and didn't seem to have any issue getting the property values out of the ExpandoObject.
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Results}" />
and
// Since there is no guarantee that all the ExpandoObjects have the
// same set of properties, get the complete list of distinct property names
// - this represents the list of columns
var rows = dataGrid1.ItemsSource.OfType<IDictionary<string, object>>();
var columns = rows.SelectMany(d => d.Keys).Distinct(StringComparer.OrdinalIgnoreCase);
foreach (string text in columns)
{
// now set up a column and binding for each property
var column = new DataGridTextColumn
{
Header = text,
Binding = new Binding(text)
};
dataGrid1.Columns.Add(column);
}
The problem here is that the clr will create columns for the ExpandoObject itself - but there is no guarantee that a group of ExpandoObjects share the same properties between each other, no rule for the engine to know which columns need to be created.
Perhaps something like Linq anonymous types would work better for you. I don't know what kind of a datagrid you are using, but binding should should be identical for all of them. Here is a simple example for the telerik datagrid.
link to telerik forums
This isn't actually truly dynamic, the types need to be known at compile time - but this is an easy way of setting something like this at runtime.
If you truly have no idea what kind of fields you will be displaying the problem gets a little more hairy. Possible solutions are:
Creating a type mapping at runtime by using Reflection.Emit, I think it's possible to create a generic value converter that would accept your query results, create a new type (and maintain a cached list), and return a list of objects. Creating a new dynamic type would follow the same algorithm as you already use for creating the ExpandoObjectsMSDN on Reflection.Emit
An old but useful article on codeproject
Using Dynamic Linq - this is probably the simpler faster way to do it.Using Dynamic Linq
Getting around anonymous type headaches with dynamic linq
With dynamic linq you can create anonymous types using a string at runtime - which you can assemble from the results of your query. Example usage from the second link:
var orders = db.Orders.Where("OrderDate > #0", DateTime.Now.AddDays(-30)).Select("new(OrderID, OrderDate)");
In any case, the basic idea is to somehow set the itemgrid to a collection of objects whose shared public properties can be found by reflection.
my answer from Dynamic column binding in Xaml
I've used an approach that follows the pattern of this pseudocode
columns = New DynamicTypeColumnList()
columns.Add(New DynamicTypeColumn("Name", GetType(String)))
dynamicType = DynamicTypeHelper.GetDynamicType(columns)
DynamicTypeHelper.GetDynamicType() generates a type with simple properties. See this post for the details on how to generate such a type
Then to actually use the type, do something like this
Dim rows as List(Of DynamicItem)
Dim row As DynamicItem = CType(Activator.CreateInstance(dynamicType), DynamicItem)
row("Name") = "Foo"
rows.Add(row)
dataGrid.DataContext = rows
Although there is an accepted answer by the OP, it uses AutoGenerateColumns="False" which is not exactly what the original question asked for. Fortunately, it can be solved with auto-generated columns as well. The key to the solution is the DynamicObject that can have both static and dynamic properties:
public class MyObject : DynamicObject, ICustomTypeDescriptor {
// The object can have "normal", usual properties if you need them:
public string Property1 { get; set; }
public int Property2 { get; set; }
public MyObject() {
}
public override IEnumerable<string> GetDynamicMemberNames() {
// in addition to the "normal" properties above,
// the object can have some dynamically generated properties
// whose list we return here:
return list_of_dynamic_property_names;
}
public override bool TryGetMember(GetMemberBinder binder, out object result) {
// for each dynamic property, we need to look up the actual value when asked:
if (<binder.Name is a correct name for your dynamic property>) {
result = <whatever data binder.Name means>
return true;
}
else {
result = null;
return false;
}
}
public override bool TrySetMember(SetMemberBinder binder, object value) {
// for each dynamic property, we need to store the actual value when asked:
if (<binder.Name is a correct name for your dynamic property>) {
<whatever storage binder.Name means> = value;
return true;
}
else
return false;
}
public PropertyDescriptorCollection GetProperties() {
// This is where we assemble *all* properties:
var collection = new List<PropertyDescriptor>();
// here, we list all "standard" properties first:
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this, true))
collection.Add(property);
// and dynamic ones second:
foreach (string name in GetDynamicMemberNames())
collection.Add(new CustomPropertyDescriptor(name, typeof(property_type), typeof(MyObject)));
return new PropertyDescriptorCollection(collection.ToArray());
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes) => TypeDescriptor.GetProperties(this, attributes, true);
public AttributeCollection GetAttributes() => TypeDescriptor.GetAttributes(this, true);
public string GetClassName() => TypeDescriptor.GetClassName(this, true);
public string GetComponentName() => TypeDescriptor.GetComponentName(this, true);
public TypeConverter GetConverter() => TypeDescriptor.GetConverter(this, true);
public EventDescriptor GetDefaultEvent() => TypeDescriptor.GetDefaultEvent(this, true);
public PropertyDescriptor GetDefaultProperty() => TypeDescriptor.GetDefaultProperty(this, true);
public object GetEditor(Type editorBaseType) => TypeDescriptor.GetEditor(this, editorBaseType, true);
public EventDescriptorCollection GetEvents() => TypeDescriptor.GetEvents(this, true);
public EventDescriptorCollection GetEvents(Attribute[] attributes) => TypeDescriptor.GetEvents(this, attributes, true);
public object GetPropertyOwner(PropertyDescriptor pd) => this;
}
For the ICustomTypeDescriptor implementation, you can mostly use the static functions of TypeDescriptor in a trivial manner. GetProperties() is the one that requires real implementation: reading the existing properties and adding your dynamic ones.
As PropertyDescriptor is abstract, you have to inherit it:
public class CustomPropertyDescriptor : PropertyDescriptor {
private Type componentType;
public CustomPropertyDescriptor(string propertyName, Type componentType)
: base(propertyName, new Attribute[] { }) {
this.componentType = componentType;
}
public CustomPropertyDescriptor(string propertyName, Type componentType, Attribute[] attrs)
: base(propertyName, attrs) {
this.componentType = componentType;
}
public override bool IsReadOnly => false;
public override Type ComponentType => componentType;
public override Type PropertyType => typeof(property_type);
public override bool CanResetValue(object component) => true;
public override void ResetValue(object component) => SetValue(component, null);
public override bool ShouldSerializeValue(object component) => true;
public override object GetValue(object component) {
return ...;
}
public override void SetValue(object component, object value) {
...
}

Resources