How to map Syncfusion Blazor QueryBuilder rules from C# class - c#

I am trying to implement the Syncfusion Blazor QueryBuilder component to build dynamic search filters.
I can successfully store query builder rules to my DB after mapping to my C# class model.
But when I try to re-map these rules back to the Syncfusion "RuleModel" class I get error below in the browser.
It appears to be caused by the dynamic property types on the "Operate" and "Value" fields.
When I get the error, these properties have the "ValueKind" element. When this is not present, it works fine (eg. If I manually create a new RuleModel())
Error in Browser, when QueryBuilderObj.SetRules() method is called..
Error: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot implicitly convert type 'System.Text.Json.JsonElement' to 'string'
at CallSite.Target(Closure , CallSite , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at Syncfusion.Blazor.QueryBuilder.Internal.QueryBuilderRules`1.SetField()
at Syncfusion.Blazor.QueryBuilder.Internal.QueryBuilderRules`1.OnParametersSetAsync()
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
Client Code
<SfQueryBuilder TValue="#FilterColumns" #ref="QueryBuilderObj" MaxGroupCount=3>
<QueryBuilderColumns>
<QueryBuilderColumn Field="Status" Label="Status" Type="ColumnType.String"></QueryBuilderColumn>
<QueryBuilderColumn Field="DepartmentCode" Label="DepartmentCode" Type="ColumnType.String"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
<button type="button" #onclick="getRules">Get Rules</button>
#code {
SfQueryBuilder<FilterColumns> QueryBuilderObj;
public class FilterColumns
{
public string Status { get; set; }
public string DepartmentCode { get; set; }
}
[Parameter]
public RuleModel rules { get; set; }
private void getRules()
{
QueryBuilderObj.SetRules(rules.Rules, rules.Condition);
}
}
Syncfusion RuleModel Class
public class RuleModel
{
public RuleModel();
public string Condition { get; set; }
public string Field { get; set; }
public string Label { get; set; }
public bool? Not { get; set; }
public dynamic Operator { get; set; }
public string Type { get; set; }
public dynamic Value { get; set; }
public List<RuleModel> Rules { get; set; }
}
I have replicated the syncfusion RuleModel class exactly as above in my domain model.
Has anyone successfully stored and retrieved QueryBuilder rules from Blazor UI into a C# model/class?
Thanks.

I figured out this problem is due to System.Text.Json behaviour causing the newly created property to have a ValueKind object on each property.
Basically this problem... How do I get System.Text.Json to deserialize objects into their original type?
When I switch to using NewtonSoft JSON.NET to de-serialize the RuleModel, it solved the issue.
Not sure how to make it work with System.Text.Json - but will use Json.Net for now.

Related

Deserialize string Property as Json Object in Azure Modile App Service Backend

I'm having a similar problem as posted here
Except I'm using an Azure Mobile App Service Backend.
I have json string stored in the sql database and I need the App Service to de-serialize it to the object type.
Currently the Json from the Azure Mobile App Service Get looks like this:
(and this isn't the in-code formatting, it's actually formatting the json that way)
{"deleted": false,"updatedAt": "2020-06-09T16:30:48.09Z","createdAt": "2020-06-03T04:34:41.617Z","version": "AAAAAABXrYA=","id": "DBEC6DE9-3C5C-47C5-8404-67C79FCF6740","equipmentSpeakerTapValue": "{\"value\": 0.0,\"wattage\": 0,\"direct\": false}"}
equipmentSpeakerTapValue is shown as a string an not a json nested object
I need the Json to look like this:
{"deleted": false,"updatedAt": "2020-06-09T16:30:48.09Z","createdAt": "2020-06-03T04:34:41.617Z","version": "AAAAAABXrYA=","id": "DBEC6DE9-3C5C-47C5-8404-67C79FCF6740","equipmentSpeakerTapValue":{"Value":2.5,"Wattage":70,"Direct":false}"}
Since I'm using Azure Mobile App Service I don't know if the problem is with the Entity Framework or the Json Serializing/DeSerializing. I also don't know how to change it since Azure Mobile App Service is a wrapper so you can't do things normally.
Here is my EF object model and the model of what I need the string property to deserialize to:
public class SiteEquipment
{
public string Id { get; set; }
public byte[] Version { get; set; }
public DateTimeOffset? CreatedAt { get; set; }
public DateTimeOffset? UpdatedAt { get; set; }
public bool Deleted { get; set; }
//I don't know which EquipmentSpeakerTapValue to use:
//string version that serializes to: "equipmentSpeakerTapValue": "{\"value\": 0.0,\"wattage\": 0,\"direct\": false}"
public string EquipmentSpeakerTapValue { get; set; }
//object version that should serialize to: "equipmentSpeakerTapValue":{"Value":2.5,"Wattage":70,"Direct":false}"
public TapValue EquipmentSpeakerTapValue { get; set; }
}
public class TapValue
{
public double Value { get; set; }
public int Wattage { get; set; }
public bool Direct { get; set; }
}
sorry I can't comment yet.
I noticed in your second string you only capitalized the last 3 "items" in the json. Do you not care if the previous are capped?
Your String Above
{"deleted": false,"updatedAt": "2020-06-09T16:30:48.09Z","createdAt": "2020-06- 03T04:34:41.617Z","version": "AAAAAABXrYA=","Id": "DBEC6DE9-3C5C-47C5-8404-67C79FCF6740","equipmentSpeakerTapValue":{"Value":2.5,"Wattage":70,"Direct":false}"}
Verification Question String
{"Deleted": false,"UpdatedAt": "2020-06-09T16:30:48.09Z","CreatedAt": "2020-06-03T04:34:41.617Z","Version": "AAAAAABXrYA=","id": "DBEC6DE9-3C5C-47C5-8404-67C79FCF6740","EquipmentSpeakerTapValue":{"Value":2.5,"Wattage":70,"Direct":false}"}
I am actually surprised your first values, such as updatedAt get deserialized properly.
What I would do is in your class definitions, add the JsonProperty attribute so that json knows to deserialize the non-backed parameter (such as Wattage doesn't have a private wattage property)
So put
public class TapValue
{
[JsonProperty("value")]
public double Value { get; set; }
[JsonProperty("wattage")]
public int Wattage { get; set; }
[JsonProperty("direct")]
public bool Direct { get; set; }
}
Are you using Newtonsoft? Because the above would tell it to map the lower case versions to the Upper case versions. Or if you had MyValueHere, you might have myValueHere in json, so make that your jsonproperty value myValueHere and it will map to MyValueHere

Issue for nested child list for Swagger

I'm developing a web api with .NET Core
I use Swagger for API documentation
For an endpoint i have a return object like this
public class TaxonomyNode
{
public int Id { get; set; }
public string Key { get; set; }
public string Title { get; set; }
public string ParentKey { get; set; }
public bool HasAssociations { get; set; }
public int Level { get; set; }
public IEnumerable<TaxonomyNode> Children { get; set; }
}
When i launch my web application on swagger page and expland my API endpoint swagger give me this error
Resolver error at paths./api/Taxonomies/{idTaxonomyItemDbMaster}/GetNodes.get.responses.200.content.application/json.schema.properties.children.items.$ref
Could not resolve reference:
Resolver error at paths./api/Taxonomies/{idTaxonomyItemDbMaster}/GetNodes.get.responses.200.content.text/json.schema.properties.children.items.$ref
Could not resolve reference:
Resolver error at paths./api/Taxonomies/{idTaxonomyItemDbMaster}/GetNodes.get.responses.200.content.text/plain.schema.properties.children.items.$ref
Could not resolve reference:
the problem is
public IEnumerable Children { get; set; }
i think that swagger go into an overflow when generating documentation for response
How can i prevent this?
thanks
Your model of type TaxonomyNode is refering to the type TaxonomyNode as a property.
Therefore it will cause a infinite loop if you wish to serialize this type.
It is the cause of the error you are getting

Is there a way to conditionally serialize C# list objects based on a property?

I have an MVC Model that generates JSON files, based off of user inputs, that are used as part of an automated workflow. The issue that I am having is figuring out how to change the order in which a list of objects are serialized based off of a specific property value.
Here is a simplified look at my model:
public class Ticket
{
public string TicketNumber { get; set; }
public string TicketName { get; set; }
public string ApplicationName { get; set; }
public IList<Jams> JamsList { get; set; }
}
public class RootObject
{
public Ticket ChangeTicket { get; set; }
}
public class JamsDestination
{
public string Dev { get; set; }
public string QA { get; set; }
public string Prod { get; set; }
}
public class Jams
{
public string TFSLocation { get; set; }
public string FileName { get; set; }
public string JamsType { get; set; }
public JamsDestination JamsLocation { get; set; }
}
(I am using Newtonsoft.Json and the SerializeObject() function in the post section of my controller)
JamsType is a drop down list populated from a sql table (Variable, Job, Trigger, and Box). What I am trying to do is ensure that any Jams change (in the list: JamsList) is serialized in an order that ensures that all Jams changes of JamsType = Box are serialized last, in order to ensure that it will run properly as a part of our automated workflow. Is there any way to accomplish this without setting up some form of Javascript function in the view to reorder them before they are indexed? (My view is a dynamic table setup so it is not guaranteed that there even will be any Jams changes each time, let alone how many are associated with a ticket).
I realized that I just needed to add Linq logic into my controller PRIOR to serializing the JSON file by doing the following:
ticket.JamsList = ticket.JamsList.OrderBy(jams => jams.JamsType == "Box").ToList();
All this actually does is just reorder the list of Jams changes to meet my conditions before it gets serialized, rather than changing the order it serializes the list (how I thought it needed to be performed).

.net core : incomplete JSON response

I'm trying to build simple API for training, in my database I got users (firstname, lastname, email password, list<sports>) and sports ( name, userID).
All is okay when I want to get my users, I got an object populated with sports. But the JSON response is incomplete, it is "cut" in the middle.
[{"firstName":"Nicolas","lastName":"Bouhours","email":"n.bouh#test.com","password":"nico#hotmail.fr","sports":[{"name":"Trail","userId":1
This is my controller :
// GET: api/Users
[HttpGet]
public IEnumerable<User> GetUsers()
{
var users = _context.Users.Include(u => u.Sports).ToList();
return users;
}
And my models :
public class Sport : BaseEntity
{
public string Name { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
public class User : BaseEntity
{
public String FirstName { get; set; }
public String LastName { get; set; }
public String Email { get; set; }
public String Password { get; set; }
public List<Sport> Sports { get; set; }
}
public class SportAppContext : DbContext
{
public SportAppContext(DbContextOptions<SportAppContext> options) : base(options)
{ }
public DbSet<User> Users { get; set; }
public DbSet<Sport> Sports { get; set; }
}
I really don't understand what happen, if you have any idea
I'm running into the same issue right now. You can also change the JSON serialization/configuration settings to ignore self-reference loops, as shown in the accepted answer for this question
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddJsonOptions(options => {
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
}
I had this problem to in one of my projects. This is caused by a self referencing loop.
You need to create some sort of DTO (Data Transfer Object) which will be used to generate your JSON.
In your DTO you remove the inverse relationship so you end up having something like
public class SportDto
{
public string Name { get; set; }
}
public class UserDto
{
public String FirstName { get; set; }
public String LastName { get; set; }
public String Email { get; set; }
public String Password { get; set; }
public List<SportDto> Sports { get; set; }
}
You then map your user User and Sport models to your UserDto and SportDto
A good tool for doing this mapping is AutoMapper. You can read the docs to see how to get started.
After the mapping is done, you Send the DTOs as your JSON and not your models.
Just to add another yet unique scenario where this can occur. This can also happen if your DAL is returning queryables. In my scenario, I was returning a boxed object from the DAL and had something like this as a linq query
...
RootLevelProp1 = "asd",
RootLevelProp2 = "asd",
Trades = b.Trades.OrderBy(c => c.Time).Select(c => new
{
c.Direction,
c.Price,
c.ShareCount,
c.Time
}) //<---- This was being returned as a queryable to the controller
The Trades query was never being executed even though it's root object had .ToListAsync() called on it. What was happening was that the controller would return the result but only up to the Trades section and the Json would not be terminated properly. I then realized an exception was being caught in some custom middleware I wrote in which it was complaining about the data reader already being open. Without going to deep into my investigation, I assumed it had to do something with the DI and how it was handling the lifecycle of the context. The fix was to just add ToList on the trades. It's an ugly way to pass data from the DAL but this is just a fun project.
In my case this solve my issue on core 3, by using Newtonsoft:
https://learn.microsoft.com/en-us/aspnet/core/web-api/advanced/formatting?view=aspnetcore-3.0#add-newtonsoftjson-based-json-format-support
Prior to ASP.NET Core 3.0, the default used JSON formatters
implemented using the Newtonsoft.Json package. In ASP.NET Core 3.0 or
later, the default JSON formatters are based on System.Text.Json.
Support for Newtonsoft.Json based formatters and features is available
by installing the Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet
package and configuring it in Startup.ConfigureServices.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddNewtonsoftJson();
}
The selected answer was correct in my case as well, my JSON response was getting truncated by a reference loop in my JSON response, and setting ReferenceLoopHandling.Ignore did indeed solve my issue. However, this is not the best solution in my opinion, as this maintains the circular references in your model. A better solution would use the [JsonIgnore] attribute within the model.
The issue in your model is here:
public class Sport : BaseEntity
{
public string Name { get; set; }
public int UserId { get; set; }
public User User { get; set; } //This is the cause of your circular reference
}
public class User : BaseEntity
{
public String FirstName { get; set; }
public String LastName { get; set; }
public String Email { get; set; }
public String Password { get; set; }
public List<Sport> Sports { get; set; }
}
As you can see, your User navigation property is where this response is truncated. Specifically, it will cause each Sport in the json response to contain all of the user information for each sport entry in the response. Newtonsoft does not like this. The solution is to simply [JsonIngore] the navigation properties that cause this circular reference. In your code this would be:
public class Sport : BaseEntity
{
public string Name { get; set; }
public int UserId { get; set; }
[JsonIgnore]
public User User { get; set; } //fixed
}
public class User : BaseEntity
{
public String FirstName { get; set; }
public String LastName { get; set; }
public String Email { get; set; }
public String Password { get; set; }
public List<Sport> Sports { get; set; }
}
Faced similar issue, response was getting truncated. Issue was a getter method which trying to formatting date.

Mongodb - entity serialized OK, but gives 'No serializer found for type' error when deserializing

I'm getting this issue using the c# driver for mongodb (v1.5)
I've had similar issues to this when serializing objects, but have always been able to resolve the in the past by registering the entity with mongodb during application startup. The document it's having a problem deserializing is nested two levels deep (ie a document embedded within a document embedded within a document).
The classes look like this:
[BsonIgnoreExtraElements]
public class FooItem : IFooItem
{
[BsonId]
public ObjectId Id { get; set; }
public IFooAccessRestrictions AccessRestrictions { get; set; }
}
public class FooAccessRestrictions : IFooAccessRestrictions
{
[BsonId]
public ObjectId Id { get; set; }
public IAccessPermission[] AccessList { get; set; }
}
public class AccessPermission : IAccessPermission
{
[BsonId]
public ObjectId Id { get; set; }
public DateTimeOffset CreatedOn { get; set; }
public ObjectId CreatedBy { get; set; }
public AccessPermissionType Type { get; set; }
public string PermittedIdentity { get; set; }
public AccessPermission()
{
}
public AccessPermission(ObjectId createdBy, AccessPermissionType type, string permittedIdentity)
{
CreatedOn = DateTime.Now;
CreatedBy = createdBy;
Type = type;
PermittedIdentity = permittedIdentity;
}
}
It's the AccessPermission class that it has the 'No serializer found for type' problem with. I've tried registering the entities with mongodb as follows on application start:
BsonClassMap.RegisterClassMap<FooAccessRestrictions>();
BsonClassMap.RegisterClassMap<AccessPermission>();
I'm guessing I must be breaking some mongodb rule here that I'm not aware of. I hope I don't need to create a custom serializer for this ... As far as I can tell I'm not doing anything I haven't done before, apart from that the document is nested two levels deep. And it is creating the document no problem at all, it's just when I try to get it back out that I have the problem.
Any help would be much appreciated.
Okay, I've managed to fix this now.
After another look at the mongodb documentation, I found out that you can register properties of a class whilst registering the class.
so now my registration looks like this:
BsonClassMap.RegisterClassMap<FooAccessRestrictions>(cm =>
{
cm.MapProperty<List<IAccessPermission>>(c => (List<IAccessPermission>)c.AccessList);
});
(Note that my property has changed from an array to a List, but this shouldn;t have any impact on the issue I'm talking about here)
MongoDB is now able to deserialize these objects as well as serialize them.

Categories