How could I annotate both property and params for a constructor value? - reflection

I defined an annotation class for serialize/deserialize a model.
If I define PROPERTY as Target and
MyClass( #PropertyName("a_name") val name: String )
I can access annotations thought my class property but not thought my class constructor params.
Viceversa if I define no Target or both PROPERTY and VALUE_PARAMETER.
For access annotation for both property and value params I should annotate like this:
#param:PropertyName("a_name")
#property:PropertyName("a_name")
But I guess nobody would like this solution.
As a workaround I should just Target the value params, then get both constructor ad properties, then match by name every constructor param with properties and access annotation from param and value from property.
It works, but I think I could find a better solution.
Any hints?

Related

Kotlin reflection: findAnnotation<Type>() returns null

I have the following data class:
data class Foo(#field:Password val password: String)
Why does Foo::password.annotations return an empty list?
Also, Foo::password.findAnnotation<Password>() returns null.
The same happens when I use an instance of Foo:
Foo("")::password.annotations
Foo("")::password.findAnnotation<Password>()
However, this java variant works: Foo::password.javaField.getAnnotation(Password::class.java).
This is on kotlin version 1.3.10.
The docs don't give much information on the inner workings of findAnnotation.
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-property/index.html
fun <T : Annotation> KAnnotatedElement.findAnnotation(): T?
Returns an annotation of the given type on this element.
What am I missing here?
Thanks in advance!
Foo::password.annotations is the list of annotations on the property. In your code, you've used the #field: use site target, which means that the annotation is applied to the backing field, not the property. Therefore, the list of annotations on the property is empty.
The Java variant works because it loads the list of annotations on the field.

How can I easily determine if JsonConvert.DeserializeObject() found and set every propery in my object? [duplicate]

In Json.NET, how do I make ALL properties required upon deserialization? I know that I can do this with attributes on the messages, but I don't want to do that. Mainly because it would require my message library to take on an external dependency.
I tried the MissingMemberHandling.Error setting, but it does the opposite of what I want. I'm okay with the JSON having extra properties. I want it to fail when any target object properties are missing in the JSON.
I'm actually deserializing to F# records, and the properties can't ordinarily be null anyway. (They can't be assigned null by normal means in code.) But Json.NET happily defaults properties to null under the covers when data is missing.
F# version of accepted answer
Resolver
open System
open Newtonsoft.Json
open Newtonsoft.Json.Serialization
type RequireAllPropertiesContractResolver() =
inherit DefaultContractResolver()
override me.CreateObjectContract(objectType:Type) =
let contract = base.CreateObjectContract(objectType)
contract.ItemRequired <- new Nullable<Required>(Required.Always)
contract
In the settings
let settings = new JsonSerializerSettings() // default settings
...
settings.ContractResolver <- new RequireAllPropertiesContractResolver()
If your model has properties that your JSON may omit, and you want that to be an error, add the attribute [JsonObject(ItemRequired=Required.Always)] to your classes:
Type: Required
A value indicating whether the object's properties are required.
Possible values for Required are:
Default: The property is not required. The default state.
AllowNull: The property must be defined in JSON but can be a null value.
Always: The property must be defined in JSON and cannot be a null value.
DisallowNull: The property is not required but it cannot be a null value [if present]. (Json.NET 8.0.1 and later.)
The setting is inherited so can be added to a generic base class.
Update
To do it globally for all objects, subclass the DefaultContractResolver and add the ItemRequired flag to all object contracts:
public class RequireObjectPropertiesContractResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
contract.ItemRequired = Required.Always;
return contract;
}
}
And then later, in settings:
var settings = new JsonSerializerSettings { ContractResolver = new RequireObjectPropertiesContractResolver() };
Notes:
If you don't want to require JSON properties when your f# member is optional see this answer to this question and also the question Json.NET make property required based on property type.
This contract resolver applies a default setting of Required.Always to all object properties, but will not override JsonProperty.AttributeRequired when applied directly. If you need that, see e.g. How to override the "Required.Always" in newtonsoft json.
As stated in the question, the setting MissingMemberHandling = MissingMemberHandling.Error solves a complimentary problem: if your JSON may have properties that your model omits, and you want that to be an error, use MissingMemberHandling.Error. See: MissingMemberHandling setting.
You may want to cache the contract resolver for best performance.
I know I am late on party here, but... Accepted answer forces all properties to be available, which could be not so good for case when your record contains Option types combining with NullValueHandling.Ignore parameter on JsonSerializerSettings. In that case you would require option type to be present, which is too limiting. We found this solution works for us:
type RequireAllPropertiesContractResolver() =
inherit CamelCasePropertyNamesContractResolver()
override __.CreateProperty(memb, serialization) =
let prop = base.CreateProperty(memb, serialization)
let isRequired = not (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() = typedefof<option<_>>)
if isRequired then prop.Required <- Required.Always
prop
I hope it helps someone.

Java bean validation: Optional fields annotation

I would like to treat some fields as Optional, if the value is null or blank don't go with the checks of the other annotated constraints.
There is some way to achieve it!
Reading this tread Java bean validation: Enforce a #Pattern only when the property is not blank don't seems cover my needs.
Below an example to explain what I mean:
public class Test {
#Max(value=100) // <--mandatory
private int parA;
#Optional // <-Custom annotation telling "do not check other if null or blank"
#Range(min=10, max=200)
private int parB;
...
}
Now you can!
https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#example-container-element-constraints-optional
public Optional<#Size(min=1, max=128) String> first_name = Optional.empty();
You cannot do what you want with Bean Validation. You cannot establish a "connection" between two constraints placed on a property. Also, if I understand correctly, your #Optional is not even a constraint annotation, but rather just a marker annotation. Note though, that all default constraints are implemented in a way that a constraint validates for null values. So in most cases you get what you want either way. In your example, you also have the problem that you are dealing with a primitive int. This will always have a value, so the range constraint would be evaluated either way.

Create a Class with full name and pass it to the custom component

I have class name : some.path.exampleclass and have to instantiate class (like getDefinitionByName()). Problem is that getDefinitionByName() makes class exampleclass and i need class with FULL name : some.path.exampleclass to pass it to custom component :
<tools:MyComp value="some.path.exampleclass"/>
Is there a quick solution for this? :)
If you're intent is to create an instance of an FXG element based on a string that represents it's fully qualified class name, you can do it like this:
var classNameString :String = // your code to get the classname;
var class :Class = getDefinitionByName( className ) as Class;
var newObject : MyObject = new class() as MyObject;
This should work perfectly fine with FXG Elements. I use it in my mobile game.
You mention that:
Setter method...need to be set with full path class
not only with one that getDefinitionByName gives me
getDefinitionByName returns an instance of Class. If your method is expecting a string that is why getDefinitionByName doesn't work.
For more specifics, if needed, you should provide the full source code for the method or property.

How does component instantiation work with respect to scope type

Even though I have specified the scope type as method, it gets instantiated in CONVERSATION scope.
>
UserHome userHome = (UserHome) Component.getInstance(UserHome.class, ScopeType.METHOD);
This is quite confusing, can someone explain this behavior?
When you call
Component.getInstance(UserHome.class, ScopeType.METHOD);
Seam internal behavior is to call
Object result = Contexts.lookupInStatefulContexts(name);
lookupInStatefulContexts API says
Search for a named attribute in all contexts, in the following order: method, event, page, conversation, session, business process, application.
As your ScopeType.METHOD does not contain your UserHome.class component, The search go on until get its scope (StypeType.CONVERSATION, right ?)
UPDATE
I was under the impression that if you specify the ScopeType to getInstance method you will be able to create the object within that scope
If the target component does not have the desired scope associated, getInstance method does not create the component within that scope. Instead it performs a hierarchical search by using Contexts.lookupInStatefulContexts till get some assigned scope
If you want more than one scope can be assigned to a component, you must scecify it by using #Role (#Roles) annotation
#Name("user")
#Scope(ScopeType.EVENT)
#Role(name="loggedUser", scope=ScopeType.SESSION)
public class User { ... }
So you specify the desired scope
Component.getInstance(User.class, ScopeType.EVENT);
or
Component.getInstance(User.class, ScopeType.SESSION);
remember Seam performs lookup by field/property name
private #In User user; // Take ScopeType.EVENT as scope
private #In User loggedUser; // Take ScopeType.SESSION as scope
I assume your UserHome class extends Seam's EntityHome class. The super class of EntityHome, which is Home, is in scope ScopeType.CONVERSATION:
#Scope(ScopeType.CONVERSATION)
public abstract class Home<T, E> extends MutableController<T>
Either you did not override the scope in your UserHome declaration or Seam ignores #Scope annotations in subclasses if one of the super classes already have an #Scope annotation.

Resources