.NET Class property as object, such as System.String - c#

I'm not sure if this is possible, or even how to properly word this.
I am creating a new webAPI that I want to use with an offline app. The app connects to the api (before going offline) and pulls down a list of fields to generate a dynamic form. I want to supply a list of fields to the client. In simple terms this might be id and description.
However, to generate the form exactly as I want it, I might want to supply a field "type" such as System.DateTime or System.String. Is it possible to do this?
In pseudocode this might be:
public class EntityType
{
public Guid EntityTypeId { get; set; }
public string Description { get; set; }
public Object Type { get; set; }
}
I might then want to create an EntityType like this:
EntityType entityType = new EntityType {
EntityTypeId = Guid.NewGuid(),
Description = "Visit Date",
Type = System.DateTime
};
It's the Type = System.DateTime I want to be able to achieve. I can then have my form generate a field where the type is date. The same might apply for strings for example.

To represent a type, use Type:
public Type Type { get; set; }
...
Type = typeof(DateTime)
However! It is unlikely that this will work with webapi, since Type is not interchangeable between platforms.
Another option is an enum of your expected types; you might find that TypeCode has everything you want, but you could also simply create your own enum of known types.

Related

JSON access field as different Schemas

I have the following JSON definitions:
public class Msg
{
[JsonProperty(PropertyName = "commandId")]
public int CommandId { get; set; }
[JsonProperty(PropertyName = "id")]
public int Id { get; set; }
[JsonProperty(PropertyName = "params")]
public JnoParams Parameters = new JnoParams();
}
public class Res_Msg
{
[JsonProperty(PropertyName = "commandId")]
public int CommandId { get; set; }
[JsonProperty(PropertyName = "id")]
public int Id { get; set; }
[JsonProperty(PropertyName = "params")]
public Dev_Result Parameters = new Dev_Result();
}
The property "params" has different JSON definitions, in the first one it is an empty structure, in the second it contains fields with values returned by the device.
When the WebSocket returns a packet to the app, I determine what to do based on the two fields "commandId" and "id" and am trying to unwrap the "params" with a different structure definition depending on the values in commandId and id.
If I had to use plain old 'c' language I would use a union, but I'm using JSON definitions.
How can I tell C# that "params" is of one type rather than another and therefore access its fields accordingly?
I thought I could convert from type1 to type2 or somehow cast it, but so far have been unsuccessful.
Thank you much for any hint.
Well;
apparently I didn't have to define every possible JSON structure in order to retrieve the values from them.
I defined the JSON for those structures I was going to use and within these structures, I only defined the fields I am interested in.
After decades spent describing structures that had to be defined exactly as the various fields in terms of type and size, it is a refreshing feeling to see that all I need really is to define the type of JSON packet and the name and type of field contained in it that I want to extract, in order for JSON to find it and return it, without any need to define in the JSON structure all the fields before it or after.

Dynamic type model binding in ASP.NET Core

I'm currently working on a content editor that can be used for multiple types of content, where a developer could specify their own model. For example, a model might look like this:
public class ImageWithCopyWidgetModel : WidgetModel, IWidgetModel
{
public string ImageUrl { get; set; }
public string ImageAltText { get; set; }
public string HeaderText { get; set; }
public string BodyContent { get; set; }
}
On the editor side, I have a view model that looks like:
public class EditContentViewModel<TModel> where TModel : IWidgetModel
{
public int Id { get; set; }
public string Name { get; set; }
public TModel WidgetModel { get; set; }
}
I have the binding on the GET/form display side working fine. My issue comes with getting the model binder to accept the data on the POST? I've tried the following, but each returns null for model.WidgetModel:
// Option 1
EditContent(int pageId, int id, EditContentViewModel<dynamic> model)
// Option 2
EditContent(int pageId, int id, EditContentViewModel<object> model)
// Option 3
EditContent(int pageId, int id, EditContentViewModel<IWidgetModel> model)
Note, for testing purposes, I tried explicitly setting the type of WidgetModel to a concrete class (the ImageWithCopyWidgetModel noted above) and that works.
I'm really trying to avoid having to use Request.Form here as its going to limit future plans for this implementation.
What you're wanting is not possible, at least out of the box. On post, all the modelbinder has is a bunch of key-value pair string. What informs its decision about how to bind those values to something useful is the action param(s). Specifically, it has no way of knowing that it should actually create an instance of ImageWithCopyWidgetModel when you're binding to EditContentViewModel.
Also, the modelbinder is designed to discard values it doesn't know what to do with. That means that it's unfortunately not even possible to cast to ImageWithCopyWidgetModel after the fact, because all properties not present on EditCopyViewModel would have been discarded by that point.
Your best bet is a custom model binder, but the implementation of that is too broad for the scope of Stack Overflow. I suggest you refer to the documentation.

Managing multiple versions of object in JSON

I have a class in C#, that has a number of variables. Let's call it "QuestionItem".
I have a list of this object, which the user modifies, and then sends it via JSON serialization (with Newtonsoft JSON library) to the server.
To do so, I deserialize the objects that are already in the server, as a List<QuestionItem>, then add this new modified object to the list, and then serialize it back to the server.
In order to display this list of QuestionItems to the user, I deserialize the JSON as my object, and display it somewhere.
Now, the problem is - that I want to change this QuestionItem and add some variables to it.
But I can't send this NewQuestionItem to the server, because the items in the server are of type OldQuestionItem.
How do I merge these two types, or convert the old type to the new one, while the users with the old version will still be able to use the app?
You are using an Object Oriented Language, so you might aswell use inheritance if possible.
Assuming your old QuestionItem to be:
[JsonObject(MemberSerialization.OptOut)]
public class QuestionItem
{
[JsonConstructor]
public QuestionItem(int Id, int Variant)
{
this.Id = Id;
this.Variant = Variant;
}
public int Id { get; }
public int Variant { get; }
public string Name { get; set; }
}
you can extend it by creating a child class:
[JsonObject(MemberSerialization.OptOut)]
public class NewQuestionItem : QuestionItem
{
private DateTime _firstAccess;
[JsonConstructor]
public NewQuestionItem(int Id, int Variant, DateTime FirstAccess) : base(Id, Variant)
{
this.FirstAccess = FirstAccess;
}
public DateTime FirstAccess { get; }
}
Note that using anything different than the default constructor for a class requires you to use the [JsonConstructor] Attribute on this constructor and every argument of said constructor must be named exactly like the corresponding JSON properties. Otherwise you will get an exception, because there is no default constructor available.
Your WebAPI will now send serialized NewQuestionItems, which can be deserialized to QuestionItems. In fact: By default, JSON.NET as with most Json libraries, will deserialize it to any object if they have at least one property in common. Just make sure that any member of the object you want to serialize/desreialize can actually be serialized.
You can test the example above with the following three lines of code:
var newQuestionItem = new NewQuestionItem(1337, 42, DateTime.Now) {Name = "Hello World!"};
var jsonString = JsonConvert.SerializeObject(newQuestionItem);
var oldQuestionItem = JsonConvert.DeserializeObject<QuestionItem>(jsonString);
and simply looking at the property values of the oldQuestionItem in the debugger.
So, this is possible as long as your NewQuestionItem only adds properties to an object and does neither remove nor modify them.
If that is the case, then your objects are different and thus, requiring completely different objects with a different URI in your API, as long as you still need to maintain the old instance on the existing URI.
Which brings us to the general architecture:
The most clean and streamline approach to what you are trying to achieve is to properly version your API.
For the purpose of this link I am assuming an Asp.NET WebApi, since you are handling the JSON in C#/.NET. This allows different controller methods to be called upon different versions and thus, making structural changes the resources your API is providing depending on the time of the implementation. Other API will provide equal or at least similar features or they can be implemented manually.
Depending on the amount and size of the actual objects and potential complexity of the request- and resultsets it might also be worth looking into wrapping requests or responses with additional information. So instead of asking for an object of type T, you ask for an Object of type QueryResult<T> with it being defined along the lines of:
[JsonObject(MemberSerialization.OptOut)]
public class QueryResult<T>
{
[JsonConstructor]
public QueryResult(T Result, ResultState State,
Dictionary<string, string> AdditionalInformation)
{
this.Result = result;
this.State = state;
this.AdditionalInformation = AdditionalInformation;
}
public T Result { get; }
public ResultState State { get; }
public Dictionary<string, string> AdditionalInformation { get; }
}
public enum ResultState : byte
{
0 = Success,
1 = Obsolete,
2 = AuthenticationError,
4 = DatabaseError,
8 = ....
}
which will allow you to ship additional information, such as api version number, api version release, links to different API endpoints, error information without changing the object type, etc.
The alternative to using a wrapper with a custom header is to fully implement the HATEOAS constraint, which is also widely used. Both can, together with proper versioning, save you most of the trouble with API changes.
How about you wrapping your OldQuestionItem as a property of QuestionItem? For example:
public class NewQuestionItem
{
public OldQuestionItem OldItem { get; set; }
public string Property1 {get; set; }
public string Property2 {get; set; }
...
}
This way you can maintain the previous version of the item, yet define new information to be returned.
Koda
You can use something like
public class OldQuestionItem
{
public DateTime UploadTimeStamp {get; set;} //if less then DateTime.Now then it QuestionItem
public string Property1 {get; set; }
public string Property2 {get; set; }
...
public OldQuestionItem(NewQuestionItem newItem)
{
//logic to convert new in old
}
}
public class NewQuestionItem : OldQuestionItem
{
}
and use UploadTimeStamp as marker to understand, what Question is it.

ResponstDTO with complex Property in ServiceStack

Havin a Response with a complex property, i want to to map to my responseDTO properly. For all basic types it works out flawlessly.
The ResponseDTO looks like this:
public class ResponseDto
{
public string Id {
get;
set;
}
public struct Refs
{
public Genre GenreDto {
get;
set;
}
public Location LocationDto {
get;
set;
}
}
public Refs References {
get;
set;
}
}
Genre and Location are both for now simple classes with simple properties (int/string)
public class GenreDto {
public string Id {
get;
set;
}
public string Name {
get;
set;
}
}
Question:
Is there any way, without changing/replacing the generic unserializer ( and more specific example) (in this example JSON ) to map such complex properties?
One specific difference to the GithubResponse example is, that i cant use a dictionry of one type, since i have different types under references. Thats why i use a struct, but this seems not to work. Maybe only IEnumerable are allowed?
Update
There is a way using lamda expressins to parse the json manually github.com/ServiceStack/ServiceStack.Text/blob/master/tests/ServiceStack.Text.Tests/UseCases/CentroidTests.cs#L136 but i would really like to avoid this, since the ResponseDTO becomes kinda useless this way - since when writing this kind of manual mapping i would no longer us Automapper to map from ResponseDto to DomainModel - i though like this abstraction and "seperation".
Thanks
I used lambda expressions to solve this issue, a more complex example would be
static public Func<JsonObject,Cart> fromJson = cart => new Cart(new CartDto {
Id = cart.Get<string>("id"),
SelectedDeliveryId = cart.Get<string>("selectedDeliveryId"),
SelectedPaymentId = cart.Get<string>("selectedPaymentId"),
Amount = cart.Get<float>("selectedPaymentId"),
AddressBilling = cart.Object("references").ArrayObjects("address_billing").FirstOrDefault().ConvertTo(AddressDto.fromJson),
AddressDelivery = cart.Object("references").ArrayObjects("address_delivery").FirstOrDefault().ConvertTo(AddressDto.fromJson),
AvailableShippingTypes = cart.Object("references").ArrayObjects("delivery").ConvertAll(ShippingTypeDto.fromJson),
AvailablePaypmentTypes = cart.Object("references").ArrayObjects("payment").ConvertAll(PaymentOptionDto.fromJson),
Tickets = cart.Object("references").ArrayObjects("ticket").ConvertAll(TicketDto.fromJson)
});
So this lamda exprpession is used to parse the JsonObject response of the request and map everything inside, even nested ressources. This works out very well and flexible
Some time ago i stumbled upon a similar problem. Actually ServiceStack works well with complex properties. The problem in my scenario was that i was fetching data from a database and was passing the objects returned from the DB provider directly to ServiceStack. The solution was to either create DTOs out of the models returned by the DB provider or invoke .ToList() on those same models.
I'm just sharing some experience with SS but may be you can specify what's not working for you. Is there an exception thrown or something else.

How to define a property with same name on two different types in ROWLEX?

If I have those two classes that have two different properties but with the same name:
[RdfSerializable]
public class Type1
{
[RdfProperty(true), Name = "title"]
public string Title { get; set; }
}
[RdfSerializable]
public class Type2
{
[RdfProperty(true), Name = "title"]
public string Title { get; set; }
}
and try to serialize them to RDF and validate them with http://www.w3.org/RDF/Validator/ service. Everything is Okay and they are correct.
But after I try to generate OWL files from those classes with OntologyExtractor.exe tool I get that message:
"Ontology extraction failed. http://test.org/1.0#title is assigned to more than one type."
This is strange message as the upper classes are correct and there are some RDF specifications that has same situation with different classes that have same named properties.
I expect it is a bug in ROWLEX. Your case is a valid one, but I assume I did not prepare for it when I wrote OntologyExtractor. I will try to release a fix as soon as possible.
EDIT: ROWLEX2.1 is released, you can download it from http://rowlex.nc3a.nato.int. Version 2.1 (among others) supports now the shared property functionality. The exact code in the question would still result the same error! To overcome that, you should alter the decoration of your code as follows:
[RdfSerializable]
public class Type1
{
[RdfProperty(true, Name = "title", ExcludeFromOntology=true)]
public string Title { get; set; }
}
[RdfSerializable]
public class Type2
{
[RdfProperty(true, Name = "title",
DomainAsType = new Type[]{typeof(Type1), typeof(Type2)})]
public string Title { get; set; }
}
Using the OntologyExtractor.exe, this code will result a OWL property of with an anonymous domain class that is the UNION of Type1 and Type2.
While this is technically perfectly correct solution, setting domains on properties limit their possible future reuse. As a solution, you might want to substitute the property domain with local restrictions. You can achieve that as follows:
[RdfSerializable]
public class Type2
{
[RdfProperty(true, Name = "title",
DomainAsType = new Type[]{typeof(Type1), typeof(Type2)},
UseLocalRestrictionInsteadOfDomain = true)]
public string Title { get; set; }
}
Should you leave UseLocalRestrictionInsteadOfDomain not set, ROWLEX chooses between domain and local restriction according to the current context.

Categories

Resources