How to Inject values from a property file into JSR Custom Annotation - java

I have a spring component that validates the values of an Entity Class,
One of the variables has a Custom Annotation whose values are supposed to be loaded from the property file, currently it says that Attribute must be constant
Here is the sample code.
I know that Spring allows to fetch properties like this
#Value("${allowedNames}")
private String names;
But I have an entity with one of the variables annotated by custom validator interface i.e. #NameValidationDefinition. I would like to pass the values from the properties file to the annotation but it gives compile time error that Attribute must be Constant which I understand as I know that
Annotations take only constants or final and static declared primitives or Strings
public Class Person {
#NameValidationDefinition(values = names)
private String name;
}
What I want to know is that is there a workaround for this to make it work?
The value from the properties file is by default casted to String but still when I create the Entity and initialize the variable as static final and pass the String in the Constructor, I get the same compile time error.
I would appreciate any kind of help on this.

You cannot have variables in annotations. That's not a limitation of Bean Validation, but of the JVM itself. See also Which types can be used for Java annotation members?
Note, in the Spring example you are giving the value is a string with a special "key" ${allowedNames} which later on gets interpolated. That's different from the value of the annotation being an actual variable as your code implies.

Related

How can I introspect a Java class's Jackson PropertyNamingStrategy when it is explicitly set by #JsonNaming?

I have some code which reads rows from a database and using Jackson ObjectMapper to convert them to objects. I am trying to make this as generic as possible, to serve as a library function.
For some particular object classes, the PropertyNamingStrategy is set explicitly via the #JsonNaming annotation to something other than how my row schema is defined (just the name casing is different, conceptually the names and data are the same).
I can use an intermediate library to convert the expected property names after I get them from the row schema to how the #JsonNaming annotation defines them. But that is very specific to one class.
Is there a way I can introspect a class type to find out what its PropertyNamingStrategy is? Or use ObjectMappper (or another Jackson utility) to find out, prior to doing the actual deserialization? That way my caller would not need to know or care about this when using my code.
Is there a way I can introspect a class type to find out what its
PropertyNamingStrategy is?
Yes, you can use the the SerializationConfig#introspectClassAnnotations method that returns a BeanDescription, gets its info and create an AnnotatedClass that will be inspected by the JacksonAnnotationIntrospector instance like below:
#JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class)
public class MyClass {}
AnnotatedClass acl = mapper.getSerializationConfig()
.introspectClassAnnotations(MyClass.class)
.getClassInfo();
JacksonAnnotationIntrospector jai = new JacksonAnnotationIntrospector();
//in this case it will prints class
//com.fasterxml.jackson.databind.PropertyNamingStrategy$KebabCaseStrategy
//In case of no annotation over the class the value will be null
System.out.println(jai.findNamingStrategy(acl));

Error while declaring the word `public` as a String variable intellij

How can I configure the IntelliJ IDE to not be so strict in the following class member fields:
String public;
String metaClass;
Currently, it shows an error about the String public member.
This is the naming I want, because of a JSON contract I must conform to.
I think you are asking why this code ...
String public
... does not compile.
Or to put it another way: "why does IntelliJ not allow this code?".
The word public is not a valid variable name in Java so no compiler (whether hosted by IntelliJ or by anything else) will not allow that code.
From the docs:
The rules and conventions for naming your variables can be summarized as follows:
...
Also keep in mind that the name you choose must not be a keyword or reserved word.
Here's the full list of keywords and reserved words.
In your question you wrote "This is i want format, because of the json objects" by which I think you mean that you have JSON which contains an attribute named ""public" and you want to map that to a Java class having an attribute named "public". If so, then you'll need to transform the JSON when deserilaising into your Java class. This is likely to be supported by whatever JSON library you are using. For example, Jackson provides the #JsonProperty and #JsonAlias annotations for this ... allowing this JSON: {"public": "true"} to be deserialised into a class with this member: #JsonAlias({ "public" }) public String publicScope.

Hibernate Annotations : No default constructor for entity

I am trying to persist the objects generated by JAXB. Here is the sample structure:
#Column(name = "reporting_identifier")
private String reportingIdentifier;
#Column(name = "apply_quiet_time")
private boolean applyQuietTime;
#Embedded
private RecipientDetailsList recipientDetailsList;
Below is the structure of RecipientDetailsList class:
#ElementCollection(targetClass=String.class)
private List<RecipientDetails> recipientDetails;
Now, the RecipientDetails class has one argument constructor, which accepts a String. That String I want to persist in the database as a part of the whole record. I am seeing
org.hibernate.InstantiationException: No default constructor for entity: RecipientDetailsList
exception when I try to save an object. I have two questions:
Do we have any work around this exception? I can't change the class as it is designed for JAXB marshalling/unmarhsalling. Can I somehow store the objects without altering the structure? Also, I am interested in only storing the first record of the list referenced by
recipientDetails as I want only one row for object. I want it to ignore the rest of the records if it has more than 1 record. Is it possible?
Is this good design to use the annotation directly into classes which are generated by JAXB? Should I create another classes (and possibly mappers/converters) just to store and retrieve the information?
For your first question: this is happening because when Hibernate tries to create a bean, it does it via reflection. It does the object creation by calling the no-arg constructor, and then using the setter methods to set the properties. You can't use a bean that doesn't have a no-arg constructor.
For the second question: if something else has generated classes for you that don't have a no-arg constructor, really your only option (if you can't modify the class) is to create a wrapper round it, or a subclass that has a no-arg constructor. I don't see any other way of doing it if you can't modify the class directly. But the subclassing should be fine as long as the class you've got has enough visibility on the methods (i.e., doesn't have private methods that you then can't get to).

how to use #XmlElement in java

I'm trying to learn how to store objects as XML files in java, but I'm having a bit of a problem.
Most tutorials that I have found have said that I should use the #XmlElement annotation with set methods, however is there another way to use them, as my objects would be easier to make using just the constructors I have for them instead of a set for each field.
The #XmlElement can also be used on the property. You will find more information in the javadoc.
The javadoc gives this example:
public class USPrice {
#XmlElement(name="itemprice")
public java.math.BigDecimal price;
}
All public fields and properties (get/set method pairs) will be treated by default as if they were annotated with #XmlElement. You can add #XmlElement on the get or set method. You can also annotate the field (instance variable). If you do you should annotate your class with #XmlAccesorType(XmlAccessType.FIELD).
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
JAXB does not currently support annotating constructors. If you are dealing with immutable objects then the following may help:
http://blog.bdoughan.com/2010/12/jaxb-and-immutable-objects.html

Enumerate Java Fields

I have a Java class with ~90 fields. I want to be able to do things to every field (generate an XML element for instance) without writing the same 5 lines of code with slight substitutions 90 times. In Objective C I think you can get to instance variables in ways similar to accessing Dictionary elements (ObjectForKey). Is there anything similar in Java such that I can get an array of the fields then do something to each of them?
Yes, it's called Reflection API.
In particular, MyClass.class.getDeclaredFields() will return a full list of fields declared by this class (see API for details)
Here's another approach: Use the Introspector API with the JDK to obtain bean-like properties of a class. This is helpful if you have getters and setters for your class and do not want to access the private fields directly.
Obtain a BeanInfo via the Introspector and get all the propertyDescriptors from it. To find getter of that property.
I'll have to admit that using this API is a bit cumbersome and reflection (suggested by Nikita Rybak) is more straight forward.
But there's a utility Apache BeanUtils that does all the hardwork internally so working with beans becomes simple.
Add:
If you are using the reflection API, I'd suggest you annotate your bean fields or your getters with a custom annotation.
public class MyClassWith90Fields {
#XmlSerialize("name")
private String screenName; // shoudl serialize as <name>...</name>
#XmlSerialize
private String email; // shoud serialize as <email>...</email>
#XmlSerializeIgnore
pirvate boolean flag; // shoud not serialize as annotated as ignore
}
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.METHOD})
public #annotation XmlSerialize {
public String value;
}
Once done, your generation code can check (using reflection) annotated fields and serialize them to XML appropriately.

Categories