I use, among others, Json.NET and NSwag and generate a swagger document. I have classes, Animal and Dog : Animal (inherits from Animal).
public class Animal
{
//properties
}
public class Dog : Animal
{
[JsonProperty(Order = -2)]
public string Name { get; set; }
}
I have property in Dog, which is Name. I want to sort Name before all of Dog's properties in the generated Swagger request schema.
Current result:
{
propertyOne: value1, //property of Animal
propertyTwo: value2, //property of Animal
Name: value3 //property of Dog
}
Desired result:
{
Name: value3, //property of Dog is sorted before everything else
propertyOne: value1,
propertyTwo: value2
}
I have done similar sorting but in Swashbuckle, might be similar functionality available in Nswag
Used SchemaFilter to sort properties.
services.AddSwaggerGen(swaggerGenOptions =>
{
.....
swaggerGenOptions.SchemaFilter<SwaggerSchemaFilter>();
}
In schemaFilter, I have used order logic to show all the basic datatype properties in their current oreder and properties which are custom classes in the end. But you can order by name or any other logic.
internal class SwaggerSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
schema.Properties = schema.Properties
.OrderBy(x => string.IsNullOrWhiteSpace(x.Value.Type) ? "z" : "a")
.ToDictionary(p2 => p2.Key, p2 => p2.Value);
}
}
For custom classes the type was null,
Also, you can get type from (SchemaFilterContext) context and use reflection to get more details.
Related
Say I have one class that looks like this:
public class Person
{
public string Name {get; set;}
public int Number {get; set;}
}
And another that looks like this:
public class Dog
{
public string Name {get; set;}
public int Number {get; set;}
}
They are two different classes, but they happen to have the exact same elements (a string called Name and an int called Number)
Is there an easy way in C# to, say, if I had an instance of Person to then create an instance of Dog with the same Name and Number?
For example if I had:
Person person = new Person();
person.Name = "George";
person.Number = 1;
I know I can't simply go:
Dog dog = person;
Because they are two different types. But is there a way in C# to check "oh, if they have the same element, set the same elements of Dog to equal that of Person.
But I feel there has to be an easier way than doing something like:
dog.Name = person.Name;
dog.Number = person.Number;
Especially if the class has a LOT of elements. Also if anyone is wondering, these two different classes are in two different pieces of the API, so I can't simply make them related either.
You can use AutoMapper:
public Dog UsingAMR(Person prs)
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Person, Dog>();
});
IMapper mapper = config.CreateMapper();
return mapper.Map<Person, Dog>(prs);
}
Then you can easily:
Person ps = new Person {Name = "John", Number = 25};
Dog dog = UsingAMR(ps);
Just don't forget to install AutoMapper first from the package manager console as mentioned in the reference:
From Tools menu click on NuGet Package Manager ==> Package Manager Console
Then type the following command:
PM> Install-Package AutoMapper
An object oriented approach.
public class Mammal
{
public Mammal(Mammal toCopy)
{
Name = toCopy.Name;
Number = toCopy.Number;
}
public string Name {get; set;}
public int Number {get; set;}
}
public class Person: Mammal
{
public Person(Mammal toCopy) {} /* will default to base constructor */
}
public class Dog: Mammal
{
public Dog(Mammal toCopy) {} /* will default to base constructor */
}
This will allow the following:
Person person = new Person();
person.Name = "George";
person.Number = 1;
Dog dog = new Dog(person);
Install AutoMapper package in your project.
As a best practice (for web applications) you can create new class (should derives from Profile) in your App_Start folder, that will contain all your mappings for your project.
namespace MyApp.App_Start
{
public class MyAppMapping : Profile
{
public MyAppMapping()
{
CreateMap<Person, Dog>();
//You can also create a reverse mapping
CreateMap<Dog, Person>();
/*You can also map claculated value for your destination.
Example: you want to append "d-" before the value that will be
mapped to Name property of the dog*/
CreateMap<Person, Dog>()
.ForMember(d => d.Days,
conf => conf.ResolveUsing(AppendDogName));
}
private static object AppendDogName(Person person)
{
return "d-" + person.Name;
}
}
}
Then Initialize your mapping inside the Application_Start method in Global.asax
protected void Application_Start()
{
Mapper.Initialize(m => m.AddProfile<MyAppMapping>());
}
You can now use the mappings that you have created
var dog = AutoMapper.Mapper.Map<Person, Dog>(person);
If you don't work with big generic list, you can do it using LinQ.
var persons = new List<Person>();
// populate data [...]
var dogs = persons.Select(p=>new Dog{Name=p.Name,Number=p.Number}).ToList();
It's easy to remember, and you can filter data previously.
How can I project into class property using NHibernate? For example:
[Test]
public void Test()
{
MyClass dto = null;
var test = CurrentSession.CreateCriteria<Contact>()
.Add(Restrictions.Eq("ContactName", "John Smith"))
.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("ContactName").WithAlias(() => dto.SubClass.Name))
.Add(Projections.Property("EmailAddress").WithAlias(() => dto.Email))
)
.SetResultTransformer(Transformers.AliasToBean<MyClass>())
.List<MyClass>();
Assert.That(test[0].SubClass.Name, Is.EqualTo("John Smith"));
}
class MyClass
{
public string Email { get; set; }
public MySubClass SubClass { get; set; }
}
class MySubClass
{
public string Name { get; set; }
}
As you see I have a simple query and want to transform 1 row into 1 object - without lists but with a subclass. Unfortunately, it fails:
NHibernate.PropertyNotFoundException : Could not find a setter for property 'Name' in class 'MyTest+MyClass'
Is it possible to achieve this behaviour without custom transformer?
The default result transformer will be able to fill the root entity properties. But we can introduce our custom result transformer. There is one I do use:
DeepTransformer<TEntity> : IResultTransformer
Which is ready to convert . notation into chain of inner objects (excluding collections)
So, if you'll take it, and reuse it, this syntax would work:
...
.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("ContactName").As("SubClass.Name"))
.Add(Projections.Property("EmailAddress").As("Email"))
)
.SetResultTransformer(DeepTransformer<MyClass>())
You can even improve it, but the idea of custom transformer should be clear now
it's possible to change representation of inherited Id field from BsonType.ObjectId to BsonType.String?
public class BaseClass
{
public string Id { get; set; }
}
public class MyClass : BaseClass
{
}
I tried this approach, but it doesn't work.
BsonClassMap.RegisterClassMap<BaseClass>(cm =>
{
cm.AutoMap();
var id = cm.GetMemberMap(c => c.Id);
cm.SetIdMember(id);
cm.IdMemberMap.SetRepresentation(BsonType.ObjectId);
});
BsonClassMap.RegisterClassMap<MyClass>(cm =>
{
cm.AutoMap();
var idMember = cm.IdMemberMap; // idMember is null
idMember.SetRepresentation(BsonType.String);
});
Thanks a lot.
No, this is not possible. Derived classes cannot change the representation of fields in their base classes. Derived classes do of course have full control over how they want their fields to be serialized.
SOLVED: I had a property defined incorrectly!
I'm getting this error...
Missing type map configuration or unsupported mapping.
Mapping types:
HouseDomain -> RoomDomain
{namespace}.HouseDomain -> {namespace}.RoomDomain
Destination path:
City.Houses.Houses.Houses0[0]
So for example, I have
public class CityDomain
{
public IEnumerable<HouseDomain> DomainHouses {get;set;}
}
public class HouseDomain
{
public IEnumerable<RoomDomain> DomainRooms {get;set;}
}
public class RoomDomain
{
//whatever
}
and
public class CityDto
{
public IEnumerable<HouseDto> DtoHouses {get;set;}
}
public class HouseDto
{
public IEnumerable<RoomDto> DtoRooms {get;set;}
}
public class RoomDto
{
//whatever
}
So I want to map CityDomain to CityDto. I have...
Mapper.CreateMap<CityDomain , CityDto>();
Is there an issue going 2 levels deep like this? Any help? Thanks!
This is all mappings you need (Automapper is smart enough to map lists of objects if mapping for appropriate object types was created):
Mapper.CreateMap<RoomDomain, RoomDto>();
Mapper.CreateMap<HouseDomain, HouseDto>()
.ForMember(d => d.DtoRooms, m => m.MapFrom(s => s.DomainRooms));
Just remove your second mapping for member DtoRooms. E.g. if house has id and room has name, then for sample domain house mapping works just fine:
HouseDomain domainHouse = new HouseDomain
{
Id = 42,
DomainRooms = new List<RoomDomain>
{
new RoomDomain { Name = "foo" },
new RoomDomain { Name = "bar" }
}
};
var dtoHouse = Mapper.Map<HouseDto>(domainHouse);
Produces:
{
Id: 42,
DtoRooms: [ { Name: "foo" }, { Name: "bar" } ]
}
Last note - make sure you create maps before you are doing mapping. Usually all maps are created on application startup.
You can keep your mappings simplier if you make names of collections the same (e.g. Rooms) for both HouseDomain and HouseDto types.
Mappings:
Mapper.CreateMap<RoomDomain, RoomDto>();
Mapper.CreateMap<HouseDomain, HouseDto>();
Types:
public class HouseDomain
{
public IEnumerable<RoomDomain> Rooms {get;set;}
}
public class HouseDto
{
public IEnumerable<RoomDto> Rooms {get;set;}
}
I answered the similar question AutoMapping nested models a while ago.
Let's suppose I have to serialize an object of a class Car in levels e.g. Internal and Public. Some of the properties in the Public level should not be serialized as they are internal.
At this moment the 'easiest' way I can think of to achieve this is by using inheritance:
class CarPublic {
public int PropX {get;set}
}
class CarInternal: CarPublic {
public string PropY {get;set}
}
Then I could
object ToSerialize() {
CarInternal car = GetCar();
if( level == Level.Public ) {
return car as CarPublic;
} else {
return car;
}
}
The result of the ToSerialize() is taken by a framework (I don't have
control over) and serialized to JSON or XML.
I omitted the XML serialization attributes for simplicity.
This feels like a hack and hacks take you only so far. Is there better way (ways?) to achieve this?
I think its clear by now, but I would like to avoid writing my own serialization methods for JSON and XML.
Thanks in advance
Tymek
==EDIT
To clarify, I want to be able to serialize multiple levels:
class Car0 {
public int PropA {get;set}
}
class Car1: Car0 {
public string PropB {get;set}
}
class Car2: Car1 {
public int PropC {get;set}
}
class Car3: Car2 {
public string PropD {get;set}
}
and
object ToSerialize( Level level ) {
Car3 car = GetCar();
switch( level ) {
case Level.Zero: return car as Car0;
case Level.One: return car as Car1;
case Level.Two: return car as Car3;
case Level.Three: return car as Car4;
}
return null;
}
== Chosen approach
I marked Marc Gravell's answer as the answer, as it provides the generic information of how C# and it's 'standard' components support what I asked for.
However I think the best approach for my problem is to use proxy classes as shown above and
have the class being serialized in this multi-level pattern with methods like shown below.
public interface ICar {
Car0 As0();
Car1 As1();
Car2 As2();
Car3 As3();
...
}
This allows keeping the Car0..3 classes very simple, with only properties, to maintain and understand.
This depends a lot on what serialization framework you are using. You mention xml and json - well, the first thing to note is that you can just decorate with:
[XmlIgnore]
public int PropX {get;set;}
or
[ScriptIgnore]
public int PropX {get;set;}
which XmlSerializer and JavascriptSerializer will respond to. If you need to make the decision on a per-instance basis, there is the ShouldSerialize* and *Specified patterns:
public bool ShouldSerializePropX() {
// return true to serialize, false to omit
}
The above is a name-based pattern, that is used by XmlSerializer and others; it has a twin:
[XmlIgnore, Browsable(false)]
public bool PropXSpecified {
get { /* return true to serialize, false to omit */ }
set { /* can just drop this value - don't need to assign */ }
}
You don't need to do anything to wire them up - they work automatically.
Different serializers allow different patterns.
In addition, sometimes you can add things like [XmlIgnore] at runtime - for example via XmlAttributeOverrides, or the equivalent for any given serializer.
You could decorate your Internal properties with a custom attribute indicating that they should be included (or ignored depending on your requirements) and then in your ToSerialize check the attribute.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class ShouldSerializeAttribute : Attribute { }
Then your resulting class definition would look like:
class Car
{
[ShouldSerialize]
public int PropX {get;set}
// This property won't be serialized because it is internal
public int PropY { get; set; }
}
You ToSerialize would look something like:
object ToSerialize()
{
Car car = GetCar();
foreach(PropertyInfo propInfo in car.GetType().GetProperties())
{
if(ShouldSerialize(propInfo))
{
return car;
}
}
}
Where ShouldSerialize could look like:
internal bool ShouldSerialize(PropertyInfo propInfo)
{
return propInfo.GetCustomAttributes(typeof(ShouldSerializeAttribute), true).FirstOrDefault() != null;
}
UPDATE
Based on #Bill's insight in the comments. If you're looking to only serialize public attributes when level is Level.Public you can achieve that effect by reflecting on the type's properties using the BindingFlags.DeclaredOnly flag:
foreach(PropertyInfo propInfo in car.GetType().GetProperties(BindingFlags.DeclaredOnly))
This should return a list of the properties declared only by the current instance of car.