I am using openapi-generator(5.0.0) to generate code from an openapi.yaml.
Java code is generated with jaxrs-jersey generator.
I have a very simple inheritance structure with a discriminator property.
...
Cat:
type: object
allOf:
- $ref: '#/components/schemas/Pet'
Dog:
type: object
allOf:
- $ref: '#/components/schemas/Pet'
Pet:
required:
- objectType
type: object
properties:
objectType:
type: string
discriminator:
propertyName: objectType
...
when I generate java code from it I get
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "objectType", visible = true)
#JsonSubTypes({ #JsonSubTypes.Type(value = Cat.class, name = "Cat"), #JsonSubTypes.Type(value = Dog.class, name = "Dog"), })
#javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen")
public class Pet {
private #Valid String objectType;
/**
*/
public Pet objectType(String objectType) {
this.objectType = objectType;
return this;
}
#ApiModelProperty(required = true, value = "")
#JsonProperty("objectType")
#NotNull
public String getObjectType() {
return objectType;
}
public void setObjectType(String objectType) {
this.objectType = objectType;
}
//....
}
For java code, the runtime type of an object is already known and the objectType property holds redundant information. Worse, if I forget to initialize it properly the class information is not serialized to json.
How can I get rid of the objectType property?
(Note: I also generate typescript code where the discriminator property is actually required)
Related
I have the following code to implement interface input parameter validation and now want to use hibernate-validator to do this
public class Order
{
private String orderNo;
private String orderId;
private String status;
private String startTime;
private String endTime;
//getter and setter...
}
public class OrderService
{
public Object search(Order order) throws Exception
{
String message = "";
if (order.getOrderId().isEmpty() && order.getOrderNo().isEmpty() && order.getStatus().isEmpty())
{
if (order.getStartTime().isEmpty() && order.getEndTime().isEmpty())
message = "xxx";
}
if (!message.isEmpty())
throw new Exception(message);
Object result = null;
// splice sql according to the attribute of order and get the result
// result = sql query result
return result;
}
}
I tried to use Hibernate-validator's group to achieve this, but if there are more parameters, I need to write a lot of groups, which seems stupid. I have more than 100 interfaces, and will be added later, using Class-level constraints would be a good idea choice?
Below is the code trying to use Hibernate-validator's group implementation:
public class Order
{
#Empty(groups = One.class)
#NotEmpty(groups = Two.class)
private String orderNo;
#Empty(groups = One.class)
#NotEmpty(groups = Three.class)
private String orderId;
#Empty(groups = One.class)
#NotEmpty(groups = Four.class)
private String status;
#NotEmpty(groups = One.class)
private String startTime;
#NotEmpty(groups = One.class)
private String endTime;
}
public class BeanValidatorUtils
{
static Validator validator;
static
{
HibernateValidatorConfiguration configuration = Validation.byProvider(HibernateValidator.class).configure();
ValidatorFactory factory = configuration.failFast(true).buildValidatorFactory();
validator = factory.getValidator();
}
public static <T> void validation(T beanParam) throws AppException
{
if (!containsGroup(beanParam, One.class))
return;
Set<ConstraintViolation<T>> validate = validator.validate(beanParam, One.class);
ConstraintViolation<T> constraintViolation = validate.iterator().next();
String firstViolationMessage = constraintViolation.getMessage();
if (!validate.isEmpty() && containsGroup(beanParam, Two.class))
{
validate = validator.validate(beanParam, Two.class);
}
if (!validate.isEmpty() && containsGroup(beanParam, Three.class))
{
validate = validator.validate(beanParam, Three.class);
}
if (!validate.isEmpty())
throw new AppException(firstViolationMessage);
}
private static boolean containsGroup(Object bean, Class<?> groupClazz)
{
// ...
}
}
Is there any other way to use Hibernate-validator to verify the Order in the search method?
As you are trying to make a validation decision based on the state of multiple properties of the Order you might want to explore these 3 options:
Class level constraint
This would mean that you have to create your own constraint annotation (let's say #ValidOrder) and a corresponding ValidOrderValidator
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy = { ValidOrderValidator.class })
#interface ValidOrder {
String message() default "{message.key}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
public class ValidOrderValidator implements ConstraintValidator<ValidOrder, Order> {
#Override
public boolean isValid(Order order, ConstraintValidatorContext constraintValidatorContext) {
//null values are valid
if ( order == null ) {
return true;
}
if (order.getOrderId().isEmpty() && order.getOrderNo().isEmpty() && order.getStatus().isEmpty()) {
if ( order.getStartTime().isEmpty() && order.getEndTime().isEmpty() ) { return false; }
}
return true;
}
}
You can also check this post for more detailed info on how to add new constraints using ServiceLoader.
#ScriptAssert constraint
If your validation logic is relatively simple and you either already have a dependency or are willing to add one for a scripting engine, you can consider using the #ScriptAssert constraint. This is similar to the previous option but you don't need to create annotations and validator implementations you just have to put script logic into this constraint:
#ScriptAssert(lang = "groovy", script = "your validation script logic")
class Order {
//...
}
#AssertTrue constraint
Last but not least, one of the easiest ways to address such validation is to use #AssertTrue constraint on a getter with validation logic inside the Order class:
class Order {
//...
#AssertTrue
public boolean isValidOrder() {
// your validation logic
}
}
Using any of these 3 approaches, you'd be able to make a validation decision based on multiple properties of the Order class.
As for validation group usage - you can leverage using the groups if you need to pass the same Order object into multiple different methods/interfaces where a different set of validation rules need to be applied in each of them. Let's say, in one case, you have to create an order, and half of the fields can be null, but then in the other - you want to update it, and everything should be present.
This question already has an answer here:
How to change all keys to lowercase when parsing JSON to a JToken
(1 answer)
Closed 5 years ago.
I have a JSON resulting from a mix of system data and user entries, like this :
{
"Properties": [{
"Type": "A",
"Name": "aaa",
"lorem ipsum": 7.1
}, {
"Type": "B",
"Name": "bbb",
"sit amet": "XYZ"
}, {
"Type": "C",
"Name": "ccc",
"abcd": false
}]
}
I need to load it, process it, and save it to MongoDB. I deserialize it to this class :
public class EntityProperty {
public string Name { get; set; }
[JsonExtensionData]
public IDictionary<string, JToken> OtherProperties { get; set; }
public string Type { get; set; }
}
The problem is that MongoDB does not allow dots in key names, but the users can do whatever they want.
So I need a way to save this additional JSON data but I also need to change the key name as it's being processed.
I tried to add [JsonConverter(typeof(CustomValuesConverter))] to the OtherProperties attribute but it seems to ignore it.
Update/Clarification: since the serialization is done by Mongo (I send the objects to the library), I need the extension data names to be fixed during deserialization.
Update
Since the fixing of names must be done during deserialization, you could generalize the LowerCasePropertyNameJsonReader from How to change all keys to lowercase when parsing JSON to a JToken by Brian Rogers to perform the necessary transformation.
First, define the following:
public class PropertyNameMappingJsonReader : JsonTextReader
{
readonly Func<string, string> nameMapper;
public PropertyNameMappingJsonReader(TextReader textReader, Func<string, string> nameMapper)
: base(textReader)
{
if (nameMapper == null)
throw new ArgumentNullException();
this.nameMapper = nameMapper;
}
public override object Value
{
get
{
if (TokenType == JsonToken.PropertyName)
return nameMapper((string)base.Value);
return base.Value;
}
}
}
public static class JsonExtensions
{
public static T DeserializeObject<T>(string json, Func<string, string> nameMapper, JsonSerializerSettings settings = null)
{
using (var textReader = new StringReader(json))
using (var jsonReader = new PropertyNameMappingJsonReader(textReader, nameMapper))
{
return JsonSerializer.CreateDefault(settings).Deserialize<T>(jsonReader);
}
}
}
Then deserialize as follows:
var root = JsonExtensions.DeserializeObject<RootObject>(json, (s) => s.Replace(".", ""));
Or, if you are deserializing from a Stream via a StreamReader you can construct your PropertyNameMappingJsonReader directly from it.
Sample fiddle.
Alternatively, you could also fix the extension data in an [OnDeserialized] callback, but I think this solution is neater because it avoids adding logic to the objects themselves.
Original Answer
Assuming you are using Json.NET 10.0.1 or later, you can create your own custom NamingStrategy, override NamingStrategy.GetExtensionDataName(), and implement the necessary fix.
First, define MongoExtensionDataSettingsNamingStrategy as follows:
public class MongoExtensionDataSettingsNamingStrategy : DefaultNamingStrategy
{
public MongoExtensionDataSettingsNamingStrategy()
: base()
{
this.ProcessExtensionDataNames = true;
}
protected string FixName(string name)
{
return name.Replace(".", "");
}
public override string GetExtensionDataName(string name)
{
if (!ProcessExtensionDataNames)
{
return name;
}
return name.Replace(".", "");
}
}
Then serialize your root object as follows:
var settings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver { NamingStrategy = new MongoExtensionDataSettingsNamingStrategy() },
};
var outputJson = JsonConvert.SerializeObject(root, settings);
Notes:
Here I am inheriting from DefaultNamingStrategy but you could inherit from CamelCaseNamingStrategy if you prefer.
The naming strategy is only invoked to remap extension data names (and dictionary keys) during serialization, not deserialization.
You may want to cache the contract resolver for best performance.
There is no built-in attribute to specify a converter for dictionary keys, as noted in this question. And in any event Json.NET would not use the JsonConverter applied to OtherProperties since the presence of the JsonExtensionData attribute supersedes the converter property.
Alternatively, if it would be more convenient to specify the naming strategy using Json.NET serialization attributes, you will need a slightly different naming strategy. First create:
public class MongoExtensionDataAttributeNamingStrategy : MongoExtensionDataSettingsNamingStrategy
{
public MongoExtensionDataAttributeNamingStrategy()
: base()
{
this.ProcessDictionaryKeys = true;
}
public override string GetDictionaryKey(string key)
{
if (!ProcessDictionaryKeys)
{
return key;
}
return FixName(key);
}
}
And modify EntityProperty as follows:
[JsonObject(NamingStrategyType = typeof(MongoExtensionDataAttributeNamingStrategy))]
public class EntityProperty
{
public string Name { get; set; }
[JsonExtensionData]
public IDictionary<string, JToken> OtherProperties { get; set; }
public string Type { get; set; }
}
The reason for the inconsistency is that, as of Json.NET 10.0.3, DefaultContractResolver uses GetDictionaryKey() when remapping extension data names using a naming strategy that is set via attributes here, but uses GetExtensionDataName() when the naming strategy is set via settings here. I have no explanation for the inconsistency; it feels like a bug.
I am trying to do some dynamic code using Entity Framework. I have a model (Model1) with one table(Test1), it's simple. What I'm trying to do is accessing the model Test1 programatically with the name of the table, to use it after in differents tasks. I was looking for in google and I have found Finding entities by key in entity framework but it's doesnt work, or I don't have any idea...
When I ran this code it breaks on trying to set entityProperty
Model1Container m = new Model1Container();
PropertyInfo entityProperty = m.GetType().GetProperties().Where(t => t.Name == "Test1").Single();
var baseQuery = (IQueryable<IIdentity>)entityProperty.GetValue(m, null);
Sorry for the explanation.
Any ideas?
You create an object with a string name and set its properties:
public class Test
{
//All my classes have these properties
//You can set up an interface and in the method you can set entity to an interface type
//You can even put these interfaces on edmx generated entities
//http://stackoverflow.com/questions/14059455/adding-validation-attributes-with-an-entity-framework-data-model
public string AString { get; set; }
public DateTime ADate { get; set; }
}
public class HomeController : Controller
{
public ActionResult IndexStackOverflow101()
{
Assembly assembly = Assembly.Load("Testy20161006");
Type t = assembly.GetType("Testy20161006.Controllers." + "Test");
Object entity = (Object)Activator.CreateInstance(t);
PropertyInfo entityProperty = t.GetProperty("AString");
PropertyInfo entityPropertyTwo = t.GetProperty("ADate");
entityProperty.SetValue(entity, Convert.ChangeType("ap", entityProperty.PropertyType), null);
entityPropertyTwo.SetValue(entity, Convert.ChangeType(DateTime.Now, entityPropertyTwo.PropertyType), null);
I have a Web API method:
public List<Task> GetTasks([FromUri] TaskFilter filter)
{
}
The method has parameter with list of nullable identifiers:
public class TaskFilter
{
public IList<int?> Assignees { get; set; }
}
When I call it:
GET /tasks?assignees=null
Server returns an error:
{
"message":"The request is invalid.",
"modelState": {
"assignees": [ "The value 'null' is not valid for Nullable`1." ]
}
}
It works only if I pass empty string:
GET /tasks?assignees=
But standard query string converters (from JQuery, Angular, etc) do not work with nulls in such way.
How to make ASP.NET to interpret 'null' as null?
Upd: The query string can contain several identifiers, e.g.:
GET /tasks?assignees=1&assignees=2&assignees=null
Upd2: JQuery converts nulls in array to empty strings, and ASP.NET interprets them as null. So the question is about calling WebAPI from Angular 1.6 ($HttpParamSerializerProvider)
Upd3: I know about workarounds, but I do not ask for them. I want a solution for specific problem:
It is a GET method
Method accepts a list from Uri
A list can contain null values
It should be List<int?> because API docs are generated automatically, and I do not want to see text array as parameter type
By default ASP.NET expects empty strings for null values (JQuery.param works in that way)
But some client libraries (e.g. Angular) does not convert null array items to empty strings
You can create a custom model bind for this specific type, inherithing from DefaultModelBinder, for sample:
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
public class TaskFilterBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, System.Web.Mvc.ModelBindingContext bindingContext)
{
var request = controllerContext.HttpContext.Request;
var assignees = request.QueryString["assignees"];
if (assignees == "null") // check if assignees is null (string) then return NULL
return null;
return assignees;
}
}
Finally we need to inform the controller as to the binding we want it to use. This we can specify using attributes
[ModelBinder(typeof(TaskFilterBinder))]
as below:
public List<Task> GetTasks([FromUri(ModelBinder=typeof(TaskFilterBinder))] TaskFilter filter)
{
// Do your stuff.
}
For more reference check this link on Custom Model Binders.
Hope, this solves your problem . Thanks
Finally, I found a solution using custom value provider:
using System;
using System.Collections.Generic;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.ValueProviders;
using System.Web.Http.ValueProviders.Providers;
using System.Globalization;
using System.Net.Http;
using System.Web.Http.ModelBinding;
public sealed class NullableValueProviderAttribute : ModelBinderAttribute
{
private readonly string[] _nullableColumns;
public NullableValueProviderAttribute(params string[] nullableColumns)
{
_nullableColumns = nullableColumns;
}
public override IEnumerable<ValueProviderFactory> GetValueProviderFactories(HttpConfiguration configuration)
{
return new ValueProviderFactory[] { new NullableValueProviderFactory(_nullableColumns) };
}
}
public class NullableValueProviderFactory : ValueProviderFactory, IUriValueProviderFactory
{
private readonly string[] _nullableColumns;
public NullableValueProviderFactory(string[] nullableColumns)
{
_nullableColumns = nullableColumns;
}
public override IValueProvider GetValueProvider(HttpActionContext actionContext)
{
return new NullableQueryStringValueProvider(actionContext, CultureInfo.InvariantCulture, _nullableColumns);
}
}
public class NullableQueryStringValueProvider : NameValuePairsValueProvider
{
private static readonly string[] _nullValues = new string[] { "null", "undefined" };
private static IEnumerable<KeyValuePair<string, string>> GetQueryNameValuePairs(HttpRequestMessage request, string[] nullableColumns)
{
foreach (var pair in request.GetQueryNameValuePairs())
{
var isNull = Array.IndexOf(nullableColumns, pair.Key) >= 0 && Array.IndexOf(_nullValues, pair.Value) >= 0;
yield return isNull ? new KeyValuePair<string, string>(pair.Key, "") : pair;
};
}
public NullableQueryStringValueProvider(HttpActionContext actionContext, CultureInfo culture, string[] nullableColumns) :
base(GetQueryNameValuePairs(actionContext.ControllerContext.Request, nullableColumns), culture)
{ }
}
And specify it in Web API action:
public List<Task> GetTasks([NullableValueProvider("assignees")] TaskFilter filter)
{
}
I've just implemented the Translator pattern discussed here and here like so...
ITranslator interface...
public interface ITranslator
{
bool CanTranslate(Type targetType, Type sourceType);
bool CanTranslate<TTarget, TSource>();
object Translate(Type targetType, object source);
TTarget Translate<TTarget>(object source);
}
Translator.cs...
public abstract class Translator<TBusinessEntity, TServiceEntity> : ITranslator where TBusinessEntity : class
where TServiceEntity : class
{
public bool CanTranslate(Type targetType, Type sourceType)
{
return (targetType == typeof(TBusinessEntity) && sourceType == typeof(TServiceEntity)) ||
(targetType == typeof(TServiceEntity) && sourceType == typeof(TBusinessEntity));
}
public bool CanTranslate<TTarget, TSource>()
{
return CanTranslate(typeof (TTarget), typeof (TSource));
}
public TTarget Translate<TTarget>(object source)
{
return (TTarget)Translate(typeof(TTarget), source);
}
public object Translate(Type targetType, object source)
{
if (targetType == typeof(TBusinessEntity))
return ServiceToBusiness((TServiceEntity)source);
if (targetType == typeof(TServiceEntity))
return BusinessToService((TBusinessEntity)source);
throw new System.ArgumentException("Invalid type passed to Translator", "targetType");
}
protected abstract TServiceEntity BusinessToService(TBusinessEntity value);
protected abstract TBusinessEntity ServiceToBusiness(TServiceEntity value);
protected abstract List<TServiceEntity> BusinessToService(List<TBusinessEntity> valueList);
protected abstract List<TBusinessEntity> ServiceToBusiness(List<TServiceEntity> valueList);
}
Here is my StudentFeeTranslator class that implements the Translator abstract methods...
public class StudentFeeTranslator : Translator<StudentFee, StudentFeeType>
{
#region Overrides of Translator<StudentFee,StudentFeeType>
protected override StudentFeeType BusinessToService(StudentFee value)
{
return new
StudentFeeType
{
StudentFeeId = value.StudentFeeRefId,
FeeId = value.FeeRefId,
StudentId = value.StudentRefId,
SchoolId = value.SchoolRefId,
FeeDate = value.AssessmentDate,
FeeAmount = value.AssessmentAmount,
Balance = value.UnpaidBalance,
FeeTypeId = value.FeeType,
Description = value.FeeDescription
};
}
protected override StudentFee ServiceToBusiness(StudentFeeType value)
{
return new
StudentFee
{
StudentFeeRefId = value.StudentFeeId,
FeeRefId = value.FeeId,
StudentRefId = value.StudentId,
SchoolRefId = value.SchoolId,
AssessmentDate = value.FeeDate,
AssessmentAmount = value.FeeAmount,
UnpaidBalance = value.Balance,
FeeType = (Byte)value.FeeTypeId,
FeeDescription = value.Description
};
}
protected override List<StudentFeeType> BusinessToService(List<StudentFee> valueList)
{
return valueList.Select(BusinessToService).ToList();
}
protected override List<StudentFee> ServiceToBusiness(List<StudentFeeType> valueList)
{
return valueList.Select(ServiceToBusiness).ToList();
}
#endregion
}
Next is my StudentFeeService class minus the irrelevant methods. Notice the Translator property tagged for injection...
public partial class StudentFeeService : IStudentFeeService
{
#region Public Members
[Dependency]
public ITranslator Translator { get; set; }
#endregion
#region Private Methods
private List<StudentFeeType> ConvertStudentFeeListToListOfStudentFeeTypes(List<StudentFee> studentFees)
{
return Translator.Translate<List<StudentFeeType>>(studentFees);
}
#endregion
}
Finally, here is the code snippet of my attempt to register the Translator class with my Unity container...
container.RegisterType(typeof (ITranslator), typeof (Translator<,>));
This attempt failed. My question is how can I register a generic abstract class with a Unity container? FYI I'm using MSUnity 2.0.
You are trying to map a non-generic interface to an open generic type. How should Unity (or any other container) guess if your service needs a StudenFeeTranslator or a RentalFeeTranslator? Both implement ITranslator and that is all the container can see.
You can register all of your concrete implementations of ITranslator giving them individual names. This is something all containers support. And then make Unity inject that specific dependency into the Translator property of your service. Something like
container.RegisterType(typeof(ITranslator), typeof(StudentFeeTranslator), "StudentFee");
container.RegisterType(typeof(ITranslator), typeof(RentalFeeTranslator), "RentalFee");
container.RegisterType(typeof(IStudentFeeService), typeof(StudentFeeService),
new InjectionProperty("Translator", new ResolvedParameter<ITranslator>("StudentFee")));
That is a lot of repetetive code though.
Unity does not come with registration conventions out-of-the-box. But the TecX project contains an enhanced configuration engine that would allow you to do something like this:
ConfigurationBuilder builder = new ConfigurationBuilder();
builder.Scan(
scanner =>
{
scanner.AddAllImplementationsOf(typeof(ITranslator);
scanner.AssembliesFromApplicationBaseDirectory();
});
container.AddExtension(builder);
This registers all implementations of ITranslator with the name of the implementing class (e.g. the name for StudentFeeTranslator would be StudentFeeTranslator) in one go.
If you make your interface generic it would be easier to inject into the property. Matching ITranslator<X, Y> to an implementation thereof is not really hard to do.