How to iterate over an array from a model - c#

I have an API which returns a reponse like this:
{
"count": 1,
"value": [
{
"id": "20",
"url": "https://dev.azure.com/xxx/_apis/wit/workItems/20"
},
{
"id": "21",
"url": "https://dev.azure.com/xxx/_apis/wit/workItems/20"
}
]
}
I am mapping the response to a C# model like this:
namespace TestApp.Models
{
public class TestSuiteModel
{
public int count { get; set; }
public List<Value> value { get; set; }
}
public class Value
{
public string id { get; set; }
public string url { get; set; }
}
}
I initialize the model in my index.razor page as:
protected override async Task OnInitializedAsync()
{
await this.GetTestSuites();
}
TestSuiteModel TestSuitesResult;
protected async Task GetTestSuites()
{
// url = ...
TestSuitesResult = await TestSuiteService.GetTestSuites(url);
await base.OnInitializedAsync();
}
TestSuitesResult works fine as of this step, in that I can do a foreach loop and see the json reponse displayed on my blazor page.
The only issue I have is when I use the library: https://github.com/Postlagerkarte/blazor-dragdrop
The library says to use the drag and drop service as:
<Dropzone Items="MyItems">
<div>#context.MyProperty</div>
</Dropzone>
MyItems is a sample array and MyProperty is an array key.
But when I use it like this, it doesn't work:
<Dropzone Items="TestSuitesResult">
<div>#context.value</div>
</Dropzone>
I get an error saying TestSuitesResult isn't valid. I don't know how else to pass the array into the component

I think Dropzone component is trying to use the entire TestSuitesResult object as an array of items, but it expects an array of values. You should assign the value property of the TestSuitesResult object to the Items property of the Dropzone component instead:
<Dropzone Items="TestSuitesResult.value">
<div>#context.id</div>
</Dropzone>
Also, it's possible that the TestSuitesResult object isn't yet populated when the Dropzone component is rendered. To solve this issue, you can consider using a loading indicator to only render the Dropzone component after the TestSuitesResult object has been populated.
#if (TestSuitesResult != null)
{
<Dropzone Items="TestSuitesResult.value">
<div>#context.value</div>
</Dropzone>
}
else
{
// ... loading
}

try this
List<Value> TestSuitesResult;
protected async Task GetTestSuites()
{
// url = ...
var model = await TestSuiteService.GetTestSuites(url);
TestSuitesResult = model.value;
await base.OnInitializedAsync();
}

When the Dropzone component is rendered, it's possible the TestSuitesResult object isn't yet populated. You also aren't targeting the List object. Try the implementation below:
#if(TestSuitesResult != null)
{
<Dropzone Items="TestSuitesResult.value">
<div>#context.url </div>
</Dropzone>
}

The dropzone component actually expects you to provide a property which implements IList<T>
You can see this when pressing F12 within Visual Studio on the parameters name. Or within the code on Github.
public IList<TItem> Items { get; set; }
So all you'll need to do is to provide your Values to the component.
#if (TestSuitesResult != null)
{
<Dropzone Items="TestSuitesResult.value">
<div>#context.id</div> #* access properties from Value class here *#
</Dropzone>
}

Related

C# appsetttings bind array of objects to class

I have int appsettings.js section like that:
"AccessKeys": {
"user1": {
"pass": ""
},
I created classes in C# to bind this section to these classes:
public class AccessKeys
{
public List Users = new List();
}
public class AccessKeyUserJson
{
public AccessKeyUser AccessKeyUser { get; set; }
}
public class AccessKeyUser
{
public string Pass { get; set; }
}
I bind above classes in Startup.cs:
services.Configure<AppSettingsConfig>(Configuration);
In AppSettingsConfig I have property AccessKeys and this property is binded correctly but Users is empty (0 items)
I changed structure:
"AccessKeys": [
{
"user1": "",
"pass": ""
},
]
Why don't you try something like this:
using (var ms = new System.IO.MemoryStream(Encoding.Unicode.GetBytes(myJSONstring)))
{
System.Runtime.Serialization.Json.DataContractJsonSerializer js = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(myStruct));
myStruct aux = (myStruct)js.ReadObject(ms);
}
This is a single level approach, and sincerely, I've never tried to cast anything in a sub-classing scheme but maybe it worths trying.
As you can see, here, the JSON string is taken by a Memory Stream and then casted into the final struct/class.

MongoDB Changestream Filters

Currently using the MongoDB.Driver v2.11.0 in a .Net Core 3.1 application. Trying to read values from a changestream and have been following the documentation here:
https://mongodb.github.io/mongo-csharp-driver/2.11/reference/driver/change_streams/
My problem is i'm trying to only get objects with a specific type and updated field, but cannot get both filters working. I can get the changestream to grab only objects that have a type of Z from the fulldocument, but cannot get any updateDescription.updatedFields filters to work properly.
Object
public class Abc
{
[BsonElement("d")]
public D D{ get; set; }
[BsonElement("e")]
public E E{ get; set; }
}
public class D
{
[BsonElement("type")]
public string Type{ get; set; }
}
public class E
{
[BsonElement("e")]
public F F{ get; set; }
}
public class F
{
[BsonElement("status")]
public string Status { get; set; }
}
So after connecting to mongo and getting the collection here is the code to setup the change stream.
protected ChangeStreamOptions _changeStreamOptions => new ChangeStreamOptions { FullDocument = ChangeStreamFullDocumentOption.UpdateLookup };
public IChangeStreamCursor<ChangeStreamDocument<Abc>> GetChangeStreamCursor()
{
return _collection.Watch(ConfigurePipeline(), _changeStreamOptions);
}
private PipelineDefinition<ChangeStreamDocument<Abc>, ChangeStreamDocument<Abc>> ConfigurePipeline()
{
List<IPipelineStageDefinition> pipeline = new List<IPipelineStageDefinition>();
pipeline.Add(PipelineStageDefinitionBuilder.Match(ConfigureFilters()));
return pipeline;
}
private FilterDefinition<ChangeStreamDocument<Abc>> ConfigureFilters()
{
var builder = Builders<ChangeStreamDocument<Abc>>.Filter;
//here is where I build the filters and having the issues.
//if its just based on the object type It works.
return builder.Eq("fullDocument.d.type", "z");
}
This works and have no problems getting only objects that had a type of Z that were updated.
If I try to then also add a filter to only return objects of the Z and that had the field Status to have been updated to a specific type.
Here is what I've tried for that:
builder.Eq("fullDocument.d.type", "z") & builder.AnyEq(x => x.UpdateDescription.UpdatedFields.Values, "Action");
builder.Eq("updateDescription.updatedFields.e.f.status", "Action");
builder.Eq("e.f.status", "Action");
The application will run with this but just never picks up any changes.
Also only tried looking at the updated fields since the UpdateDescription.UpdatedFields is an array.
builder.ElemMatch(x => x.UpdateDescription.UpdatedFields, x => x.Name == "e.f.status" && x.Value == "Action")
This fails in the program main with this error
Unable to determine the serialization information for x => x.UpdateDescription.UpdatedFields.'
Simply put I need to create a change stream and only get back objects that have a specific property type in the fulldocument and where another property was updated to a specific type.
Any help would be appreciated!
Edit Update:
If I change the types to be a BsonDocument I can pass this as the pipeline and it works:
So I got it working with a BsonObject and using this:
var filter = "{ $and: [ { operationType: 'update' }, " +
"{ 'fullDocument.d.type' : 'z'}" +
"{ 'updateDescription.updatedFields': { 'e.f.status': 'Action' } } ] }";
But I would like to keep it typed if possible.
So I figured out a solution that is working for me the way I described above.
private FilterDefinition<ChangeStreamDocument<Abc>> ConfigureFilters()
{
var builder = Builders<ChangeStreamDocument<Abc>>.Filter;
var filters = builder.And(builder.Eq("fullDocument.d.type", "z") & new BsonDocument("e.f.status", new BsonDocument("$eq", "Action")));
return filters;
}

How to directly map an object class from angular 2 to to web api using JSON?

I have a model class I created in angular 2 to track fields. This same class also exists as a model in my webapi project.
export class People {
Name: string;
Phone: string;
Date: date;}
export class Address {
street: string,
zip: string,
}
in my service I send this to my webapi controller
getPeopleData(peopleModel: People, addressmodel: Address)
{
let headers = new headers(...)
data = {
"p": peopleModel,
"a": addressModel
}
let body = JSON.stringify(data);
return this.http.post(url, body, {headers: headers})
.map((response: Response)=> ...
}
finally in my controller
public JArray GetPeopleData([FromBody]JObject models)
{
var modelPeople = models["p"].ToObject<People>();
var modelAddress = models["a"].ToObject<Address>();
}
modelPeople and modeAddress doesn't map. How can I get my model to map directly.
All I get are a bunch of null fields when there is a string of data in the JObject.
EDIT:
I created a container class that holds the objects of People and Address
public class Container
{
public People people {get; set;}
public Address addresss {get; set;}
}
I then passed in the object to the Container and my results are still null
public JArray GetPeopleData([FromBody]Container container)
{
var modelPeople = container.people;
var modelAddress = container.address;
}
both have all values of null.
I dont get it I feel like I am so close but something is missing
Hello it works for me
//This is a Gobal object
result = {
modelPeople: '',
modelAddress: ''
}
constructor( private _http: Http ) {
}
getJsonModel() {
promise = await this._http.get(URL).toPromise().then(
res => {
this.result= res.json();
}
);
}

Collections Editor is not persisting entries in custom web control markup in design view in VS 2013?

I am trying to develop a simple custom web control for ASP.Net WebForms that has a collections property called Subscriptions.
I can compile the control project successfully and add it from toolbox to an aspx page without any issues.
The problem is when I add entries for Subscriptions property using the collections editor in design view in Visual Studio 2013.
I can input multiple Subscriptions but when I click on OK button of the collections editor and then I go back to Subscriptions property in design view it's empty even though I had input some entries a moment ago.
Markup of custom control in aspx
<cc1:WebControl1 ID="WebControl1" runat="server"></cc1:WebControl1>
Question : What is not correct with my code that is causing the collections to not show up in control's markup in design view?
Custom web control code
namespace WebControl1
{
[ToolboxData("<{0}:WebControl1 runat=\"server\"> </{0}:WebControl1>")]
[ParseChildren(true)]
[PersistChildren(false)]
public class WebControl1 : WebControl
{
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("")]
[Localizable(true)]
public string Text
{
get
{
String s = (String)ViewState["Text"];
return ((s == null) ? "[" + this.ID + "]" : s);
}
set
{
ViewState["Text"] = value;
}
}
[
Category("Behavior"),
Description("The subscriptions collection"),
DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
Editor(typeof(SubscriptionCollectionEditor), typeof(UITypeEditor)),
PersistenceMode(PersistenceMode.InnerDefaultProperty)
]
public List<Subscription> Subscriptions { get; set; }
protected override void RenderContents(HtmlTextWriter output)
{
output.Write(Text);
}
}
}
[TypeConverter(typeof(ExpandableObjectConverter))]
public class Subscription
{
private string name;
private decimal amount;
public Subscription()
: this(String.Empty, 0.00m)
{
}
public Subscription(string nm, decimal amt)
{
name = nm;
amount = amt;
}
[
Category("Behavior"),
DefaultValue(""),
Description("Name of subscription"),
NotifyParentProperty(true),
]
public String Name
{
get
{
return name;
}
set
{
name = value;
}
}
[
Category("Behavior"),
DefaultValue("0.00"),
Description("Amount for subscription"),
NotifyParentProperty(true)
]
public decimal Amount
{
get
{
return amount;
}
set
{
amount = value;
}
}
}
public class SubscriptionCollectionEditor : System.ComponentModel.Design.CollectionEditor
{
public SubscriptionCollectionEditor(Type type)
: base(type)
{
}
protected override bool CanSelectMultipleInstances()
{
return false;
}
protected override Type CreateCollectionItemType()
{
return typeof(Subscription);
}
}
I was able to solve the problem by making following 2 changes.
Since for collections like List the .Net framework will automatically display an appropriate editor so we don't need to specify the editor since the collection is of List type. So we don't need this attribute Editor(typeof(SubscriptionCollectionEditor), typeof(UITypeEditor)).
The setter for Subscriptions needs to be removed and only a get should be there as in code below. If a setter is used then it should be used as in second code snippet. But automatic get and set should not be used with collection property in a custom web control.
The final code for the collections property should look like below and then collections will not disappear when one returns to it later on in design-time VS 2013.
Code that works without a setter
private List<Subscription> list = null;
[Category("Behavior"),
Description("The subscriptions collection"),
DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
PersistenceMode(PersistenceMode.InnerDefaultProperty)
]
public List<Subscription> SubscriptionList
{
get
{
if (lists == null)
{
lists = new List<Subscription>();
}
return lists;
}
}
Code that works with a setter
[Category("Behavior"),
Description("The subscriptions collection"),
DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
PersistenceMode(PersistenceMode.InnerDefaultProperty)
]
public List<Subscription> SubscriptionList
{
get
{
object s = ViewState["SubscriptionList"];
if ( s == null)
{
ViewState["SubscriptionList"] = new List<Subscription>();
}
return (List<Subscription>) ViewState["SubscriptionList"];
}
set
{
ViewState["SubscriptionList"] = value;
}
}

Templated serialization of C# objects to JSON

I need to serialize objects to JSON. I would like to do it with a template instead of using data annotations (as most frameworks do). Does anybody know a good way of doing this?
A picture says more than 1000 words. I'm looking for something that looks like this:
For example, if I had a class like this:
public class Test
{
public string Key { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public Test Related { get; set; }
}
And a had template string that could look like this:
{
id: "$Key",
name: "$Name",
related: "$Related.Name"
}
I want to get a JSON object, whose properties are filled in according to Key, Name and Related.Name of the object.
Basically I'm searching for a JSON serialization method that supports templating instead.
I don't know about any library that does this for you, but it's not that hard to build it yourself.
If you have your template, you need to parse it as JSON and then replace all of the placeholders with actual values. To do that, you can use the visitor pattern.
Since JSON.NET (the JSON library I'm using) doesn't seem to have a visitor, you can create one yourself:
abstract class JsonVisitor
{
public virtual JToken Visit(JToken token)
{
var clone = token.DeepClone();
return VisitInternal(clone);
}
protected virtual JToken VisitInternal(JToken token)
{
switch (token.Type)
{
case JTokenType.Object:
return VisitObject((JObject)token);
case JTokenType.Property:
return VisitProperty((JProperty)token);
case JTokenType.Array:
return VisitArray((JArray)token);
case JTokenType.String:
case JTokenType.Integer:
case JTokenType.Float:
case JTokenType.Date:
case JTokenType.Boolean:
case JTokenType.Null:
return VisitValue((JValue)token);
default:
throw new InvalidOperationException();
}
}
protected virtual JToken VisitObject(JObject obj)
{
foreach (var property in obj.Properties())
VisitInternal(property);
return obj;
}
protected virtual JToken VisitProperty(JProperty property)
{
VisitInternal(property.Value);
return property;
}
protected virtual JToken VisitArray(JArray array)
{
foreach (var item in array)
VisitInternal(item);
return array;
}
protected virtual JToken VisitValue(JValue value)
{
return value;
}
}
And then create a specialized visitor that replaces the placeholders with actual values:
class JsonTemplateVisitor : JsonVisitor
{
private readonly object m_data;
private JsonTemplateVisitor(object data)
{
m_data = data;
}
public static JToken Serialize(object data, string templateString)
{
return Serialize(
data, (JToken)JsonConvert.DeserializeObject(templateString));
}
public static JToken Serialize(object data, JToken template)
{
var visitor = new JsonTemplateVisitor(data);
return visitor.Visit(template);
}
protected override JToken VisitValue(JValue value)
{
if (value.Type == JTokenType.String)
{
var s = (string)value.Value;
if (s.StartsWith("$"))
{
string path = s.Substring(1);
var newValue = GetValue(m_data, path);
var newValueToken = new JValue(newValue);
value.Replace(newValueToken);
return newValueToken;
}
}
return value;
}
private static object GetValue(object data, string path)
{
var parts = path.Split('.');
foreach (var part in parts)
{
if (data == null)
break;
data = data.GetType()
.GetProperty(part)
.GetValue(data, null);
}
return data;
}
}
The usage is then simple. For example, with the following template:
{
id : "$Key",
name: "$Name",
additionalInfo:
{
related: [ "$Related.Name" ]
}
}
You can use code like this:
JsonTemplateVisitor.Serialize(data, templateString)
The result then looks like this:
{
"id": "someKey",
"name": "Isaac",
"additionalInfo": {
"related": [
"Arthur"
]
}
}
You might want to add some error-checking, but other than that, the code should work. Also, it uses reflection, so it might not be suitable if performance is important.
10 years have passed since I've posted the question. Since I've been working with Node.JS and discovered Handlebars and how it is pretty easy to get it to parse JSON instead of HTML template. The Handlebars project has been converted to .NET.
You can use a special ITextEncoder to let Handlebars generate JSON:
using HandlebarsDotNet;
using System.Text;
public class JsonTextEncoder : ITextEncoder
{
public void Encode(StringBuilder text, TextWriter target)
{
Encode(text.ToString(), target);
}
public void Encode(string text, TextWriter target)
{
if (text == null || text == "") return;
text = System.Web.HttpUtility.JavaScriptStringEncode(text);
target.Write(text);
}
public void Encode<T>(T text, TextWriter target) where T : IEnumerator<char>
{
var str = text?.ToString();
if (str == null) return;
Encode(str, target);
}
}
Let's see it in action:
using HandlebarsDotNet;
var handlebars = Handlebars.Create();
handlebars.Configuration.TextEncoder = new JsonTextEncoder();
var sourceTemplate = #"{
""id"": ""{{Key}}"",
""name"": ""{{Name}}"",
""related "": ""{{Related.Name}}""
}";
var template = handlebars.Compile(sourceTemplate);
var json = template(new
{
Key = "Alpha",
Name = "Beta",
Related = new
{
Name = "Gamme"
}
});
Console.WriteLine(json);
This will write the following:
{
"id": "Alpha",
"name": "Beta",
"related ": "Gamme"
}
I did a small write-up on the topic on my blog: Handlebars.Net & JSON templates. In this blog I also discuss how to improve debugging these templates.
You can also use a Text Template file for your json template . The template engine will fill in the blanks and return you the result.
If you are using Visual Studio,
Create a .tt file ,
Mark it with TextTemplatingFilePreprocessor in Custom Tool property of the file. This will create a new class for you that takes care of processing the template.
For integrating your data in the resulted string , extend the newly generated class in a separate file , in which you pass the data (the arbitrary class from you image).
Use this to get the json formatted code;
MyData data = ...;
MyTemplatePage page = new MyTemplatePage(data);
String pageContent = page.TransformText();
Now the pageContent have the json formatted string; For more details about how to handle the .tt file , look here : Text Template Control Blocks
I had exactly the same need. I needed an end user (technical users but not developers) to be able to create their own json files that can later be filled via data.
Microsoft Teams is doing something similar with their adaptive card website:
https://adaptivecards.io/designer/
On the bottom left there is a json "template" and on the bottom right a json to load into the template.
Conclusion: Despite extensive research I have not found any .NET library doing this.
Sorry (๑•́ㅿ•̀๑).
Screenshot of adaptive card designer

Categories

Resources