I'm putting together a web API that needs to match an external sources XML format and was looking to rename the Data Type objects in the swagger output.
It's working fine on the members of the class but I was wondering if it was possible to override the class name as well.
Example:
[DataContract(Name="OVERRIDECLASSNAME")]
public class TestItem
{
[DataMember(Name="OVERRIDETHIS")]
public string toOverride {get; set;}
}
In the generated output I end up seeing
Model:
TestItem {
OVERRIDETHIS (string, optional)
}
I'd hope to see
OVERRIDECLASSNAME {
OVERRIDETHIS (string, optional)
}
Is this possible?
Thanks,
I had the same problem and I think I solved it now.
First of all add SchemaId in Swagger Configuration (from version 5.2.2 see https://github.com/domaindrivendev/Swashbuckle/issues/457):
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.SchemaId(schemaIdStrategy);
[...]
}
Then add this method:
private static string schemaIdStrategy(Type currentClass)
{
string returnedValue = currentClass.Name;
foreach (var customAttributeData in currentClass.CustomAttributes)
{
if (customAttributeData.AttributeType.Name.ToLower() == "datacontractattribute")
{
foreach (var argument in customAttributeData.NamedArguments)
{
if (argument.MemberName.ToLower() == "name")
{
returnedValue = argument.TypedValue.Value.ToString();
}
}
}
}
return returnedValue;
}
Hope it helps.
Pretty old question, but as I was looking for a similar solution, I bumped into this.
I think the code in Vincent's answer might not work.
Here is my take on it:
private static string schemaIdStrategy(Type currentClass)
{
var dataContractAttribute = currentClass.GetCustomAttribute<DataContractAttribute>();
return dataContractAttribute != null && dataContractAttribute.Name != null ? dataContractAttribute.Name : currentClass.Name;
}
Adding to the thread as I am not able to use the answer with Swashbukle for AspNetCore.
I am doing this. However I am not totally happy as if the object is contain in another object it is showing its original name. For example if you have a result set that is Paged That result is shown incorrectly.So this is not a final answer but might work on simple use cases.
I am using a Schema Filter. And the object just have [JsonObject(Title="CustomName")] as I get the Title property for the data type.
First Define a class like this:
public class CustomNameSchema : ISchemaFilter
{
public void Apply(Schema schema, SchemaFilterContext context)
{
if (schema?.Properties == null)
{
return;
}
var objAttribute = context.SystemType.GetCustomAttribute<JsonObjectAttribute>();
if( objAttribute!= default && objAttribute?.Title?.Length > 0)
{
schema.Title = objAttribute.Title;
}
}
}
On the startup you must configure a SchemaFilter
c.SchemaFilter<CustomNameSchema>();
Related
I'm playing around and developed a simple custom JsonConverter that takes a min and max temperature and have decorated my model class as follows and validates that the temperature falls in that range.
[JsonConverter(typeof(TemperatureConverter), 5, 10)]
public int Temperature { get; set; }
This is all good but I'm wondering what's the approach to best output the correct decoration in my swagger file generated by swashbuckle... like so:
name: Temperature
schema:
type: integer
minimum: 5
maximum: 10
I know this is a trivial example, but it's more the approach to tying JsonConverter to the generation of the swagger I'm interested in.
I'm currently looking at ISchemaFilter but can't see how I can get the type of converter that decorates the property.
Thanks
You have to be at the parent schema level, looking at it's properties. By the time it gets to the property itself, it is too late, as there is no link back to the parent class.
I was using a custom attribute, not JsonConverter, but something like this should work for detecting the attribute.
public class TemperatureSchemaFilter : ISchemaFilter
{
public void Apply(Schema schema, SchemaFilterContext context)
{
var converterProperties = context.SystemType.GetProperties().Where(
prop => prop.CustomAttributes.Select(
attr => attr.AttributeType == typeof(JsonConverterAttribute)).Any()
).ToList();
foreach (var converterProperty in converterProperties)
{
var converterAttribute = (JsonConverterAttribute)Attribute.GetCustomAttribute(converterProperty.PropertyType, typeof(JsonConverterAttribute));
if (converterAttribute.ConverterType != typeof(TemperatureConverter)) continue;
Schema propertySchema = null;
try
{
propertySchema = schema.Properties.First(x => x.Key.ToLower().Equals(converterProperty.Name.ToLower())).Value;
}
catch (Exception)
{
continue;
}
if (propertySchema == null) continue;
propertySchema.Minimum = (double) converterAttribute.ConverterParameters[0];
propertySchema.Maximum = (double) converterAttribute.ConverterParameters[1];
}
}
}
Unfortunately my environment is currently hosed, so I can't test it out, but I think this is the way to go.
I'd like to make wrapper to implement simple data binding pattern -- while some data have been modified all registered handlers are got notified. I have started with this (for js target):
class Main {
public static function main() {
var target = new Some();
var binding = new Bindable(target);
binding.one = 5;
// binding.two = 0.12; // intentionally unset field
binding.three = []; // wrong type
binding.four = 'str'; // no such field in wrapped class
trace(binding.one, binding.two, binding.three, binding.four, binding.five);
// outputs: 5, null, [], str, null
trace(target.one, target.two, target.three);
// outputs: 5, null, []
}
}
class Some {
public var one:Int;
public var two:Float;
public var three:Bool;
public function new() {}
}
abstract Bindable<TClass>(TClass) {
public inline function new(source) { this = source; }
#:op(a.b) public function setField<T>(name:String, value:T) {
Reflect.setField(this, name, value);
// TODO notify handlers
return value;
}
#:op(a.b) public function getField<T>(name:String):T {
return cast Reflect.field(this, name);
}
}
So I have some frustrating issues: interface of wrapped object doesn't expose to wrapper, so there's no auto completion or strict type checking, some necessary attributes can be easily omitted or even misspelled.
Is it possible to fix my solution or should I better move to the macros?
I almost suggested here to open an issue regarding this problem. Because some time ago, there was a #:followWithAbstracts meta available for abstracts, which could be (or maybe was?) used to forward fields and call #:op(a.b) at the same time. But that's not really necessary, Haxe is powerful enough already.
abstract Binding<TClass>(TClass) {
public function new(source:TClass) { this = source; }
#:op(a.b) public function setField<T>(name:String, value:T) {
Reflect.setField(this, name, value);
// TODO notify handlers
trace("set: $name -> $value");
return value;
}
#:op(a.b) public function getField<T>(name:String):T {
trace("get: $name");
return cast Reflect.field(this, name);
}
}
#:forward
#:multiType
abstract Bindable<TClass>(TClass) {
public function new(source:TClass);
#:to function to(t:TClass) return new Binding(t);
}
We use here multiType abstract to forward fields, but resolved type is actually regular abstract. In effect, you have completion working and #:op(a.b) called at the same time.
You need #:forward meta on your abstract. However, this will not make auto-completion working unless you remove #:op(A.B) because it shadows forwarded fields.
EDIT: it seems that shadowing happened first time I added #:forward to your abstract, afterwards auto-completion worked just fine.
I am trying to access a different context with one variable. Please have a look at the code before:
...
private readonly ClientOneType _contextClientOne;
private readonly ClientTwoType _contextClientTwo;
public ExampleService()
{
_contextClientOne = new ClientOneType();
_contextClientTwo = new ClientTwoType();
}
public Stores[] GetStores(Store storeModel)
{
try
{
var _dynamicContext = null; //this throws an error because c# needs a type for runtime.
if (client == "OutBack")
_dynamicContext = _contextClientOne;
else if(client == "DollarGeneral")
_dynamicContext = _contextClientTwo;
var stores = (from s in _dynamicContext.Store //this is where the magic should take place
where s.StoreName == storeModel.StoreName
select p).ToArray();
return stores;
}
...
}
I get an error when running this because _dynamicContext can not be null so how can i create a variable that can be changed into different contexts?
The lazy solution would be to create different methods for each client, but that wouldn't be very effective as it will become unmaintainable.
I will really appreciate the help. Thank you in advance.
public interface IClientType
{
public Store Store { get; }
}
public class ClientOneType : IClientType
{
...
}
public class ClientTwoType : IClientType
{
...
}
public Stores[] GetStores(Store storeModel)
{
try
{
IClientType _dynamicContext = null;
...
Do ClientOneType and ClientTwoType both derrive from a base class that exposes the property named "Store" ?
I'm guessing they do not, and since they do not, there is no way to use the same variable to write the LINQ query you are writing because the compiler has to be able to determine what properties are available.
however, you could use IQueryable to dynamically build the query
IQueryable<Stores> storeQry=null;
if (client == "Walmart")
storeQry= _contextClientOne.Store.AsQueryable();
else if(client == "CHS")
storeQry= _contextClientTwo.Store.AsQueryable();
var stores = (from s in storeQry
where s.StoreName == storeModel.StoreName
select p).ToArray();
I have code like this:
//Fields
Product _prod, _existingProd;
void Test()
{
_prod = MakeAndPopulateSomeRandomProduct();
_existingProd = GetProdFromDb(1);
Mapper.CreateMap()
.AfterMap((s, d) =>
{
Console.WriteLine(d==_existingProd); //Why does this print false?
//Customize other properties on destination object
});
Mapper.Map(_prod, _existingProd);
}
When I call Test(), false is printed but I expected true. In my scenario, it is important to be able to access the original destination object via the AfterMap argument. I only included the fields to demonstrate the problem but in my real code, I don't have direct access to them. How can I access the object instances passed in to Map() when customizing the mapping?
The following example works. Probably you are using some type converter which creates new instance... Also please provide all mapping configurations to better understand the problem.
[TestFixture]
public class AfterMap_Test
{
//Fields
private Product _prod, _existingProd;
[Test]
public void Test()
{
Mapper.CreateMap<Product, Product>()
.AfterMap((s, d) =>
{
Trace.WriteLine(d == _existingProd); //Why does this print false?
//Customize other properties on destination object
});
_existingProd = new Product {P1 = "Destination"};
_prod = new Product {P1 = "Source"};
Mapper.Map(_prod, _existingProd);
}
}
internal class Product
{
public string P1 { get; set; }
}
I have many Flex objects like this one:
public class MyData {
public var time: Date;
public var label: String;
}
I am populating this object from a DB record retrieved via AMF that looks something like this:
{
label: "Label",
incident: "2009-08-15 11:12:14.12233"
}
I want to write a generic value mapper for these object that, given a target object (instance of MyData here) and an input record, will be able to tell that MyData.time is a Date field and perform type mapping automatically. Something like this:
function map(obj, targetType): * {
var newInstance: * = new targetType();
for (var property: String in obj) {
if (getPropertyType(targetType, property) == Date) {
newInstance[property] = parseDate(obj[property]);
}
else {
newInstance[property] = obj[property];
}
}
}
function getPropertyType(type_var: Class, property: String): Class {
// .. this is what I have no idea how to do
}
Can someone fill in the blank here?
You possibly need something like describeType. And maybe you need to use getDefinitionByName() if you want to make to a real object. So something like this for the contents of your function:
var typeXml:XML = describeType(type_var[property]);
return getDefinitionByName(typeXml.type[0].#name);
I haven't compiled it. Just throwing it out there to see if it helps.
You can use the 'is' operator to check the type of an object.
The is operator
function map(obj, targetType): * {
var newInstance: * = new targetType();
for (var property: String in obj) {
if (obj[property] is Date) {
newInstance[property] = parseDate(obj[property]);
}
else {
newInstance[property] = obj[property];
}
}
}
hth
Koen
If you need to map an Object variable to a variable class as MyData you can do the following
public class MyData
{
public var time: Date;
public var label: String;
function map(obj:Object):void
{
for (var property: String in obj)
{
this[property] = obj[property];
}
}
}
Note: The object obj must contain the exact "time" and "label" properties.
Hope it solves your problem