Is it possible to customize the Swagger Schema? - c#

Is it possible to customize the generate Swagger documentation for ASP.NET CORE (C#)? Specifically, it seems to be changing the order properties are displayed in my model (i.e. it puts derived class properties first).
class BaseObj
{
string Username {get;set;}
string Password {get;set;}
}
class Obj2 : BaseObj
{
string SomeotherProp {get;set;}
}
Swagger generates:
SomeotherProp
Username
Password
I want SomeotherProp to be at the bottom. I've tried using the Display(Order=1) attribute, but Swagger ignores that. I didn't see any hook in the configuration that I can custom sort.

After a lot of research and trial and error, I stumbled across the answer. Swagger doesn't actually reflect upon your types directly, rather it uses Json.Net to get the type schema. Json.Net respects the JsonProperty attribute. So, on my base type, I can set the JsonProperty=-2 on the properties to get them to show first. Note that you have to use -2 and not -1 since that is reserved. By using -2, you don't have to set JsonProperty on all the derived types.
This will work in my case, but I found another post where the guy defined a custom contract resolver and sorted the properties there... that'll be more generic and cleaner then JsonProperty. Need to figure out how to hook that into Asp.net core though.
But anyways, the point is, the funky ordering is coming from Json.net and not Swagger.

Related

How to show the 'discriminator' in generated documentation by Nswag?

we have developed an api and used the tool Nswag to automatically generate the Swagger api documentation. We have some endpoints in our api, where we want to update some fields by using inheritance. For nearer explanation, we have one update method (like POST api/person/{id}) where the user provide a json in the body and by giving the discriminator, the program knows the type, can deserialize the json string and use the right update method, like UpdateAddress or something. When the user does not give this information, then the deserialized object in our client is null and results in errors.
Now there is a problem, that the generated Swagger documentation does not show the discriminator 'property'. It rightly visualizes the inheritance structure with the properties by using this approach:
[JsonConverter(typeof(JsonInheritanceConverter), "discriminator")]
[KnownType(typeof(PersonUpdateAddressCommand))]
public class PersonCommand : CommandBase
{
}
The user does not know, that he have to provide the discriminator property, until we say this to him, but the documentation should be self-explanatory in best case.
To solve this, I added a public string property in the CommandBase class with the name 'discriminator':
public abstract class CommandBase
{
public string discriminator { get; set; }
}
Now it would visualize the property in the documentation, but this seems a bit over the top, because this discriminator 'property' is already existing somewhere in the heap, so why define an extra property?
Is there a way, to show the discriminator in the generated swagger documentation without defining an extra property? Or is this the right approach to add a string property?
The underlying library NJsonschema does not support system.text.json yet hence it doesn't work unless you use json.net as your serializer

Can you create a custom Attribute that functions like several?

Say you have a property in a model that looks like this:
[DataType(DataType.DateTime)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime CreationDate { get; set; }
Could you do something like this:
[ComboAttribute] // Does the same thing as the two attributes above
public DateTime CreationDate { get; set; }
I'm exploring ways to ensure all my DateTime properties have the same set of 4 to 5 attributes. If I have to add or change an attribute, currently I have to use Ctrl + F and make sure I get every instance, which is just asking for trouble.
Is this possible? Is there a better way to get the error-proofing I'm looking for?
Attributes are data structures attached a type or member definition. At runtime, components can use reflection APIs to find out the attributes which have been applied to a type or member and adjust their behaviour accordingly.
It's important to understand that attributes themselves don't do anything. There always has to be a piece of code looking for that specific attribute. If you define a new attribute, the code will not find it unless it is looking for attributes matching a specific convention or inheriting from a special base type. You need to understand the code looking for the attributes in order to understand whether you can create your own.
For your specific task of creating an aggregation of attributes, the only thing that can work is a pre-processor (such as PostSharp) that can take your custom attribute and literally re-write the code as though you had put the two attributes there.
If you do go down the PostSharp route, there's an example of generating attributes using custom attributes right here: How to inject an attribute using a PostSharp attribute?
You have two issues here: one, that your attribute will be looked up by whatever framework is expecting it (e.g. MVC is expecting some attributes on properties). Two, that your attribute will perform the logic of all attributes combined.
For the first problem, your attribute will have to inherit from known base attributes, like ValidationAttribute, or implement a known interface, like IActionFilter, depending on what attributes you want to replace.
For the second problem, your attribute can instantiate an instance of each attribute it is replacing, and delegate the functionality it inherited/implemented to the appropriate attribute instance.

How can I change properties' names (of auto generated classes) in serialization?

I have a class like below, auto generated by Entity Framework, based in our database:
public partial class TB_Cliente
{
public int IDCliente { get; set; }
public string Nome { get; set; }
// other properties
}
I'm using DataContractJsonSerializer and I need to change the properties' names in serialization. For instance, the property IDCliente must be serialized like ClientID.
I can't use [DataMember] in top of the property, because the class is auto generated, and any future changes will generate the class again and these changes will be lost.
I've had the same problem in the past, when I wanted to use data annotations. I've found the below solution, creating another file and using an interface, which works perfectly:
public interface ITB_Cliente
{
[Required]
string Nome { get; set; }
// other properties
}
[MetadataType(typeof(ITB_Cliente))]
public partial class TB_Cliente : ITB_Cliente
{
}
But this solution doesn't help me now, because (as far as I know) this attribute must be set directly in the class. I've tried to set it in the interface and it didn't work.
Is there a way to change the properties' names in the serialization, in my case? Any help will be greatly appreciated.
You probably want to use DTOs for serialization. I have not tried but AutoMapper can probably do the heavy lifting for you.
I have been trying to overcome a similar problem this week for JSON output from some legacy VB.Net classes that I would prefer not to change if I can avoid it. The serialisation is returning underlying private member names rather than the public property names, e.g. "mFirstName".
Also for autogenerated property names I am getting json like
{"k__BackingField":"Brian","k__BackingField":"Furlong"}
which is not good.
I considered a similar approach to Pawel's above (create DTOs and use Automapper which I have used extensively before).
I am also checking to see if I can make a customised json serialiser but haven't got very far yet.
The third way I have investigated is to create an "Aspect" using PostSharp which will decorate the business entity classes with the DataContract.
This would allow me to create the necessary [DataContract] and [DataMember] attributes on the public properties at compile time without having to modify the legacy code base. As I am using the legacy assemblies within a new WebAPI assembly it effectively extends the code for me.
For guidance / hints please refer to the following links:
For background information http://pietschsoft.com/post/2008/02/NET-35-JSON-Serialization-using-the-DataContractJsonSerializer
For the question that gave the pointer: How to inject an attribute using a PostSharp attribute?
For a walkthrough on how to do something similar which is enough to get going on this: http://www.postsharp.net/blog/post/PostSharp-Principals-Day-12-e28093-Aspect-Providers-e28093-Part-1

JSON.NET - exclude properties of a specific type at runtime

I'm wondering how to exclude/strip certain properties of given type(s) (or collections of those) from being serialized using Json.NET library?
I tried to write my own contract resolver (inheriting from DefaultContractResolver) with no luck.
I know that I could be done using DataAnnotations, decorating the excluded properties with ScriptIgnoreAttribute, but it's not applicable in my scenario. The objects serialized can be virtually anything, so I don't know which properties to exclude at design-time. I know only the types of properties that should not be serialized.
It looks like a rather simple task, but unfortunately I couldn't find a decent solution anywhere...
BTW - I'm not bound to Json.NET library - if it can easily be done with default/other .NET JSON serializers it'd be an equally good solution for me.
UPDATE
The properties has to be excluded before trying to serialize them. Why?
Basically, the types of objects I'm receiving and serializing can have dynamic properties of type inheriting from IDynamicMetaObjectProvider. I'm not going to describe all the details, but the DynamicMetaObject returned from GetMetaObject method of these objects doesn't have DynamicMetaObject.GetDynamicMemberNames method implemented (throws NotImplementedException...). Summarizing - the problem is those objects (I need to exclude) doesn't allow to enumerate their properties, what Json.NET serializer tries to do behind the scenes. I always end up with NotImplementedException being thrown.
I have tried both the WCF JSON serialization as well as the System.Web.Script.Serialization.JavaScriptSerializer. I have found if you want solid control of the serialization process and do not want to be bound by attributes and hacks to make things work, the JavaScriptSerializer is the way to go. It is included in the .NET stack and allows you to create and register JavaScriptConverter subclasses to perform custom serialization of types.
The only restriction I have found that may cause you a problem is that you cannot easily register a converter to convert all subclasses of Object (aka, one converter to rule them all). You really need to have knowledge of common base classes or preregister the set of types up front by scanning an assembly. However, property serialization is entirely left up to you, so you can decide using simple reflection which properties to serialize and how.
Plus, the default serialization is much much much better for JSON than the WCF approach. By default, all types are serializable without attributes, enums serialize by name, string-key dictionaries serialize as JSON objects, lists serialize as arrays, etc. But for obvious reasons, such as circular trees, even the default behavior needs assistance from time to time.
In my case, I was supporting a client-API that did not exactly match the server class structure, and we wanted a much simpler JSON syntax that was easy on the eyes, and the JavaScriptSerializer did the trick every time. Just let me know if you need some code samples to get started.
Create your own contract resolver, override the method that creates the properties for an object and then filter the results to only include those that you want.
Have you considered using the ShouldSerialize prefix property to exclude the property of your specific type at runtime?
public class Employee
{
public string Name { get; set; }
public Employee Manager { get; set; }
public bool ShouldSerializeManager()
{
return (Manager != this);
}
}

Is there any way to change a class' attributes?

I am using Microsoft REST Stark Kit Preview 2 to explore REST Collection WCF Service. Within the Service.svc.cs class, there are some classes and interfaces being used as base or component classes. For example:
public interface ICollectionService<TItem> where TItem : class
{
...
[WebHelp(Comment = "Returns the items in the collection in JSON format, along with URI links to each item.")]
[WebGet(UriTemplate = "?format=json", ResponseFormat = WebMessageFormat.Json)]
ItemInfoList<TItem> GetItemsInJoson();
...
}
[CollectionDataContract(Name = "ItemInfoList", Namespace = "")]
public class ItemInfoList<TItem> : List<ItemInfo<TItem>> where TItem : class
...
where ICollectionServices and ItemInfoList are all in Microsoft.ServiceModel.Web.dll in the Preview 2. I would change those item's attributes such as WebHelp's Comment and CollectionDataContract's Name so that I could customize help message and templates for xml node names. The Preview 2's change with embedding those interfaces and classes in its dll makes it difficult to do any customization.
So my question is that if there is any way to change a class or interface's attributes or overwrite their existing attributes so that I don't need to get the source codes to make changes?
No, you can't.
What you might be able to do is inherit from the classes. If the attributes in question are not inheritable, you can add your own to your subclasses to override them.
I checked the CollectionDataContractAttribute, and it, at least, is not inheritable. That means if you create a subclass, you can apply a different CollectionDataContract attribute to that subclass.
[CollectionDataContract(Name = "MyItemInfoList", Namespace = "MyNamespace")]
public class MyItemInfoList<TItem> : ItemInfoList<TItem> where TItem : class
However, with members, this approach will only work if they are virtual, so you can override them.
Attributes are burnt in at compile time, and cannot for reflection be set at runtime. There are a few things you can do in "ComponentModel", but they wouldn't apply here. The only other common case here is for "i18n", where an attribute might be designed to pick up different values at runtime (from resx etc, based on a key rather than the description being set in the code) - and again, this doesn't apply in this case.
So, no.
In terms of REST Start kit Preview 2's customization issue, it look like the customization was disabled when the template basic classes are moved to its core dll. According to WCF REST Start kit forum at ASP.NET, the customization features will be back in the next release (soon).

Categories

Resources