String parsing to a model (RegEx model binder?) - c#

Is there any elegant way to bind a string to a model, short of writing a bunch of boilerplate code to do the binding myself? Essentially I have a command line interface to a 3rd party device, via telnet. I am building a web service to expose this CLI to external applications. After issuing the command via telnet, I get a response string back, and need to parse this into a model.
Example:
/> echosnd -if 1 -dn 5551234567
Echo Sounder Test created on Interface CITYCOTTNNN-5ES, Resource 12 with ID #2967232
/>
And I want to bind that string to create the following object:
public class ConfigureTestResponse
{
[CLIResponse(RegEx = "Interface (?<interface>[a-zA-Z0-9]*")]
public string Interface { get; set; }
[CLIResponse(RegEx = "Resource (?<resource>[0-9]*)")]
public int Resource { get; set; }
[CLIResponse(RegEx = "#(?<id>[0-9]*)")]
public int TestId { get; set; }
}
To start, I created the CLIResponseAttribute to hold the RegEx filter for that property (although the filters above very possibly don't work - I stuck them in for illustration, only TestId is tested [a little]). I can get them split into groups using Regex, and set the properties that way, but there is a bunch of conversions that I would need to handle (int32, int64, decimal, string, complex model, etc...) based on what the property type is. I am hoping there is some sort of binding that I could provide a name/value pair to, and it would do the appropriate binding - or something even better...
Is there anything out like that? I though of using the MVC DefaultModelBinder, but there would be a good amount of overhead to use it. It would be nice to handle complex types (and lists) that are flattened in a string. What I don't want to do it write specific parsers for each command, as each returns the result in slightly different formats.
Thanks!
(Edit: I would like to move the regex into an attribute on the class, to be able to do an IsMatch on the string. Using groups, I assume I could still bind with the output to the properties.)

Nope, no magic that does it for you there. Better get started! :)

Related

How can I map JSON body properties as dynamic parameters of a .NET Core web API action method with validation?

I have to build a .NET Core REST API and I have about two dozen endpoints that take in simple JSON objects like:-
{
"foo": 23,
"bar": "bar_value"
}
and
{
"foo": 12,
"baz": true
}
etc.
Some properties, such as foo above, are common among several endpoints but have different validation requirements. In some endpoints they are required, in others, they are not and so on. I can't change these JSON payloads as they're generated by a third party I don't have any control over.
How can I map these parameters to endpoints in a .NET Core API method directly, without a class?
I can, of course, create a class for each endpoint, such as
public class SomeObject
{
[Required]
[Range(0, 100)]
public int? Foo { get; set; }
public string bar { get; set; }
}
public class SomeOtherObject
{
public int? Foo { get; set; }
[Required]
public bool Baz { get; set; }
}
...
Note the different validation rules.
But I don't feel like creating some two dozen classes. I'd much rather just specify them directly in the endpoint method:
[HttpPut]
[Route("/some-route")]
public IActionResult SomeAction([Required, Range(0, 100)] int? foo, byte? bar)
{
...
}
[HttpPut]
[Route("/some-other-route")]
public IActionResult SomeOtherAction(int? foo, [Required] baz)
{
...
}
It would be much easier to read and figure out which property is required and when by just looking at the methods instead of opening one of two dozen similarly named class files or opening one single file with two dozen similarly named classes with properties of the same name.
So how can I get .NET Core to parse the JSON and assign the property values to the action method parameters?
I'm not aware of a direct answer to this question as specified, so I'll answer this with an alternative approach as an XY problem based on your statement "It would be much easier to read and figure out which property is required and when by just looking at the methods".
This assumes there's not an easy way document your own API surface area if you're using classes. In your example, you're already writing a large amount of logic in the method signature itself, not to mention potential behaviors for default values, etc., that can make those signatures progressively harder to read and understand, and that's exactly what input model classes and model validation are designed to help encapsulate. Furthermore, now that you've decomposed the model into its parts, it becomes increasingly complex to handle validation issues as a cohesive model, regardless of whether it could be done. By accepting the entire object at once, you can run a ModelState.IsValid check, aggregate errors, or add your own and quickly return that from the controller.
By adding XML documentation to your endpoint methods and input model classes, you also open up the easy path of adding a Swagger page with Swashbuckle, which will provide a simple way for you to inspect what the model value types are and which ones are required, etc., as well as example JSON bodies in the Swagger page itself with full documentation as to the purpose of all the parameters.
While you do end up with a bunch of model classes, it's just a button press away from Visual Studio to hop to your class and see your validation requirements and input types while "in code". If class generation is frustrating, you can quickly drop your JSON samples into a class generator online and get a "pretty good" starting point for the input models: https://json2csharp.com/

Distinct event types on same collection - MongoDB and .NET Core

I want to use MongoDB to store domain events in a system written with .NET Core and C#.
I've googled a little about this, and it seems it is a common practice to have a single collection called events and simply store all events there. I've also seem people to create one field type to distinguish events. An example of this is Slide 66 of this presentation.
So if I wanted to save one UserCreated event I would add it with type user-created, and so forth.
Now I'm in doubt with respect to the mapping when it comes to using .NET Core.
Two distinct events will in general have different schema, so I think that the automatic mapping would do no good. Of course I could use the option of ignoring extra elements. But it may be the case that two events have subsets of properties which are equal, for example, all of them will have a OccurredOn DateTime. I think this could be an issue.
My idea was to query the field type. Something like:
colection.Find(BsonDocument.Parse("{type: user-created}"))
But I don't know if that is the best option, or if there is a way to set up a mapping so that the MongoDrive knows that whenever we try to get an instance of UserCreated it should look just for that type, and when we try to insert, it should create the correct type field.
In that case: given that we save distinct event types to the same collection, what is the correct approach to map this into the right C# event objects?
You could use a container like this one.
public class DomainEventContainer
{
public ObjectId Id { get; set; }
public string Type { get; set; }
public string EventData { get; set; }
}
And then based on the value of DomainEventContainer.Type, you could deserialize EventData into your desired type.

.Net Core Binding to a Specific Model given a Specific QueryString Value

I'm in the middle of refactoring an analytics api which needs to allow clients to send events as HTTP GET requests, with their "Hit" data encoded in the URL's QueryString Parameters.
My API's job is to recieve these events, and then place the "valid" events onto a queue in another part of the system for processing.
Some Hits have the same shape. The only thing that makes them different is the value of the type parameter, which all events must have at a minimum.
The problem I've encountered is that based on the Hit type, I'd like to be able to assume the type of each field given to me, which requires model binding. Of course. Currently, I can only find out what model to validate against after checking the value of type - which risks making the API excessively "stringly typed"
An example route would be:
GET https://my.anonymousanalytics.net/capture?type=startAction&amount=300&so_rep=true
Therefore, my Hit would be:
{
type: "startAction",
amount: 300,
so_rep: true
}
Which, hypothetically, could be bound to the Model StackOverflowStartHitModel
class StackOverflowStartHitModel {
public string type { get; } // Q: Could I force the value of this to be "startAction"?
? public int amount { get; }
public boolean so_rep { get; }
}
Why am I asking this here? Well I'm normally a JavaScript developer, but everyone who I'd normally turn to for C# wisdom is off work with the flu.
I have experimented with the [FromQuery] attribute decorator, but my concern is that for Hits that are the exact same shape, I might not be able to tell the difference between whether it is a startAction or an endAction, for example.
you're going to need to have a validation engine of some sort, but do not confuse this with your UI model validation. It sounds like you really have one model with a number of valid states which really is business logic.
Your model looks like this:
public class StackOverflowModel
{
public string type { get; set;}
public int amount { get; set; }
public bool so_rep { get; set;}
}
it doesn't matter what value your type field has and you don't need to hard-code it either, it will be captured as is and then it can be checked against valid states.
There are a number of ways to do this, that I can think of.
One option would be to create a list of valid rules ( states ) and then simply check if your input model matches any of them. One way to implement something like this could be with a library like FluentValidation. You can see an example here: Validation Rules and Business Rules in MVC
Another option would be to use some sort of Pattern Matching techniques like described here: https://learn.microsoft.com/en-us/dotnet/csharp/pattern-matching
Whichever option you go with, make sure you put this validation stuff in a separate class, maybe even a separate project. You can then add tests for each rule that you have to make sure everything works. This will also keep your controller light.
You haven't given examples of valid and invalid states, but I am guessing you're really talking about variations of those 3 parameters such as, when type is "something" then amount can only be < 200 and so_rep can only be "whatever". This can be done quite nicely with the FluentValidation library.

Get a specific part of my JSON data in C#

I have this JSON:
{
"response":
{
"data":
[
{
"start":1,
"subjects":["A"]
},
{
"start":3,
"subjects":["B"]
},
{
"start":2,
"subjects":["C"]
}
]
}
}
And I want to get only the "subject" data from the object with it's "start" value to be the smallest one that is greater than 1.3, which in this case would be C. Would anybody happen to know how such a thing can be achieved using C#?
I want to extend a bit on the other answers and shed more light into the subject.
A JSON -- JavaScript Object Notation - is just a way to move data "on a wire". Inside .NET, you shouldn't really consider your object to be a JSON, although you may colloquially refer to a data structure as such.
Having said that, what is a JSON "inside" .NET? It's your call. You can, for instance treat it as a string, but you will have a hard time doing this operation of finding a specific node based on certain parameters/rules.
Since a JSON is a tree-like structure, you could build your on data structure or use the many available on the web. This is great if you are learning the workings of the language and programming in general, bad if you are doing this professionally because you will probably be reinventing the wheel. And parsing the JSON is not a easy thing to do (again, good exercise).
So, the most time-effective way of doing? You have two options:
Use a dynamic object to represent your JSON data. A dynamic is a "extension" to .NET (actually, an extension to the CLR, that is called DLR) which lets you create objects that doesn't have classes (they can be considered to be "untyped", or, better, to use duck typing).
Use a typed structure that you defined to hold your data. This is the canonical, object-oriented, .NET way of doing it, but there's a trade-off in declaring classes and typing everything, which is costly in terms of time. The payoff is that you get better intellisense, performance (DLR objects are slower than traditional objects) and more safe code.
To go with the first approach, you can refer to #YouneS answer. You need to add a dependency to your project, Newtonsoft.Json (a nuget), and call deserialize to convert the JSON string to a dynamic object. As you can see from his answer, you can access properties in this object as you would access then on a JavaScript language. But you'll also realize that you have no intellisense and things such as myObj.unexistentField = "asd" will be allowed. That is the nature of dynamic typed objects.
The second approach is to declare all types. Again, this is time consuming and on many cases you'll prefer not to do it. Refer to Microsoft Docs to get more insight.
You should first create your data contracts, as below (forgive me for any typos, I'm not compiling the code).
[DataContract]
class DataItem
{
[DataMember]
public string Start { get; set; }
[DataMember]
public string[] Subjects { get; set; }
}
[DataContract]
class ResponseItem
{
[DataMember]
public DateItem[] Data { get; set; }
}
[DataContract]
class ResponseContract
{
[DataMember]
public ResponseItem Response { get; set; }
}
Once you have all those data structures declared, deserialize your json to it:
using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
var deserializer = new DataContractJsonSerializer(typeof(ResponseContract));
return (T)deserializer.ReadObject(ms);
}
The code above may seem a bit complicated, but follow a bit of .NET / BCL standards. The DataContractJsonSerializer work only with streams, so you need to open a stream that contains your string. So you create a memory stream with all the bytes from the json string.
You can also use Newtonsoft to do that, which is much simpler but, of course, still requires that extra dependency:
DataContract contract = JsonConvert.DeserializeObject<DataContract>(output);
If you use this approach you don't need the annotations (all those DataMember and DataContract) on your classes, making code a bit more clean. I very much prefer using this approach than DataContractJsonSerializer, but it's your call.
I've talked a lot about serializing and deserializing objects, but your question was, "How do I find a certain node?". All the discussion above was just a prerequisite.
There are, again and as usual, a few ways of achieving what you want:
#YouneS answer. It's very straightforward and achieves what you are looking for.
Use the second approach above, and then use your typed object to get what you want. For instance:
var contract = JsonConvert.DeserializeObject<DataContract>(output);
var query = from dataItem in contract.Response.Data
where dataItem.Start > 1.3
order by dataItem.Start;
var item = query.FirstOrNull();
Which will return the first item which, since it's ordered, should be the smallest. Remember to test the result for null.
You can use a feature from Newtonsoft that enables to directly find the node you want. Refer to the documentation. A warning, it's a bit advanced and probably overkill for simple cases.
You can make it work with something like the following code :
// Dynamic object that will hold your Deserialized json string
dynamic myObj = JsonConvert.DeserializeObject<dynamic>(YOUR-JSON-STRING);
// Will hold the value you are looking for
string[] mySubjectValue = "";
// Looking for your subject value
foreach(var o in myObj.response.data) {
if(o.start > 1.3)
mySubjectValue = o.subjects;
}

How do I use ValidateDecimal attribute in Castle.ActiveRecord?

I have a class that inherits ActiveRecordValidationBase that contains the following property:
[Property]
[ValidateDecimal]
public Decimal UnitCost { get; set; }
I also have a UnitCostTextBox that accepts input for said UnitCost.
What I would like to do is perform validation once using Castle's validators. However, it seems that before I can pass UnitCodeTextBox.Text to my object, I will need to first convert it to a decimal first.
If I have an erroneous input, an exception will be thrown. So this means I still have to perform regex validations and converting the string to a decimal type before handing it over to Castle.ActiveRecord.
Doesn't this mean it's redundant to have a [ValidateDecimal] since I've already sanitized UnitCost?
I'm wondering how do you guys do it? I have googled for examples, but most of them only handle [ValidateNonEmpty] or [ValidateEmail] which are all strings anyway, not different data types
What you're missing is the binding part (actually you're doing it manually). Castle.Components.Binder and Castle.Components.Validator are used together to automatically bind and validate string input (e.g. an HTML form) into strongly typed objects.
MonoRail does this with the [DataBind] attribute and the AR-specific [ARDataBind]
In a WebForms application you'll have to implement binding+validation yourself (you can of course use Castle.Components.Binder and Castle.Components.Validator)

Categories