How to call class method by java annotation name with Jackson? - java

Wondering if there is a way to call the getter methods by the Jackson annotation property name (eg. "value") instead of the method name (eg. getName()) or point me to the right direction?
public class Person {
private String name;
#JsonProperty("value")
public String getName() {
return name;
}
#JsonProperty("value")
public void setSet(String name) {
this.name = name;
}
}
My goal is to call multiple methods by iterating trough a list of java annotation property names.

If you really want to identify and call the methods directly you could use reflection. Something like (with no exception management):
SomeObject object = ...;
Class<?> type = object.getClass();
for (Method method : type.getMethods()) {
JsonProperty property = method.getAnnotation(JsonProperty.class);
if (property != null && property.value().equals("value")) {
if (method.getParameterCount() == 0) {
Object value = method.invoke(object);
...
}
}
}

This is what I used as an answer by Allen D.
Map<String,Object> map = new ObjectMapper.convertValue(person, new TypeReference<Map<String,Object>>(){});
String s = (String) map.get("value");

Related

Get the value of the annotated variable

Is it possible to get the variable which is annotated? I have a variable like this below:
#Flag
FlagElements flagElements = new FlagElements("key1", "type1", "value1", "desc1");
FlagElements is defined as below:
public class FlagElements<T>{
public String key;
public String type;
public T value;
public String description;
public FlagElements(String key, String type, T value, String description) {
this.key = key;
this.type = type;
this.value = value;
this.description = description;
}
}
I want to retrieve the value of flagElements. Is it possible?
You can achieve this using reflection on your class fields this way you can check if field are annotated with #Flag for instance, bellow a simple example :
for(Field field : TestObject.class.getDeclaredFields())
{
if (field.isAnnotationPresent(Flag.class))
{
Object value = field.get(objectInstance);//objectInstance is an instance of FlagElements, you can instanciate it using the new operator if you know already know the class type or use reflection if you don't know what you'll have as a class.
}
}
But make sure your Flag annotation has RetentionPolicy.RUNTIME

Setting Default value to a variable when deserializing using gson

I am trying to convert JSON to Java object. When a certain value of a pair is null, it should be set with some default value.
Here is my POJO:
public class Student {
String rollNo;
String name;
String contact;
String school;
public String getRollNo() {
return rollNo;
}
public void setRollNo(String rollNo) {
this.rollNo = rollNo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
}
Example JSON object:
{
"rollNo":"123", "name":"Tony", "school":null
}
So if school is null, I should make this into a default value, such as "school":"XXX". How can I configure this with Gson while deserializing the objects?
If the null is in the JSON, Gson is going to override any defaults you might set in the POJO. You could go to the trouble of creating a custom deserializer, but that might be overkill in this case.
I think the easiest (and, arguably best given your use case) thing to do is the equivalent of Lazy Loading. For example:
private static final String DEFAULT_SCHOOL = "ABC Elementary";
public String getSchool() {
if (school == null) school == DEFAULT_SCHOOL;
return school;
}
public void setSchool(String school) {
if (school == null) this.school = DEFAULT_SCHOOL;
else this.school = school;
}
Note: The big problem with this solution is that in order to change the Defaults, you have to change the code. If you want the default value to be customizable, you should go with the custom deserializer as linked above.
I think that the way to do this is to either write your no-args constructor to fill in default values, or use a custom instance creator. The deserializer should then replace the default values for all attributes in the JSON object being deserialized.
I was having the same issue, until I found this great solution.
For reference, you can create a post-processing class:
interface PostProcessable {
fun gsonPostProcess()
}
class PostProcessingEnabler : TypeAdapterFactory {
fun <T> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T> {
val delegate = gson.getDelegateAdapter(this, type)
return object : TypeAdapter<T>() {
#Throws(IOException::class)
fun write(out: JsonWriter, value: T) {
delegate.write(out, value)
}
#Throws(IOException::class)
fun read(`in`: JsonReader): T {
val obj = delegate.read(`in`)
if (obj is PostProcessable) {
(obj as PostProcessable).gsonPostProcess()
}
return obj
}
}
}
}
Register it like this:
GsonBuilder().registerTypeAdapterFactory(PostProcessingEnabler())
Implement it on your model:
class MyClass : Serializable, PostProcessable {
// All your variable data
override fun gsonPostProcess() {
// All your post processing logic you like on your object
// set default value for example
}
}
And finally use it when converting json string:
var myObject = myGson.fromJson(myObjectJson, MyClass::class)
Or using retrofit2:
val api = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(
GsonConverterFactory.create(
GsonBuilder().registerTypeAdapterFactory(
GsonPostProcessingEnabler()
).create()
)
)
.client(OkHttpClient.Builder().build())
.build()
.create(AccountApi::class.java)
You can simply make a universal function that checks for null
model.SchoolName= stringNullChecker(model.SchoolName);
public static String stringNullChecker(String val) {
if (null == val) val = "";
return val;
}

Get attribute using annotation value

I want to use the method execute() of the following class:
public class Parser {
#Header("header1")
private String attribute1;
#Header("header2")
private String attribute2;
#Header("header3")
private String attribute3;
#Header("header4")
private String attribute4;
public String execute(String headerValue) {
//Execute
}
}
What I want this method to achieve is matching the headerValue parameter with one in the list of #Header annotations, and returning the value of the respective attribute. For example, if I call execute("header3"), it should return the value of attribute3
How can I achieve this? Or is it a better way to code this requirement?
Why don't you just use a map for this? You'd need one anyways in order to store the mapping of the annotation parameter value to the field but if you can do this without reflection it should be easier to code and to maintain.
What I mean is:
Map<String, String> attributes; //initialized
attributes.put("header1", value1);
...
In execute() you then just access the map.
You could improve this using an enum, e.g. in order to restrict the number of possible values.
Something like this:
enum HeaderType {
HEADER1,
HEADER2,
...
}
private Map<HeaderType, String> headerAttribs = ...;
void setAttrib( HeaderType type, String value ) {
headerAttribs.put(type, value);
}
String getAttrib( HeaderType type ) {
return headerAttribs.get(type);
}
public String execute(HeaderType type ) {
//Execute
}
If you need to use a string for the header type you could consider employing an additional map string->header type to look up the correct type first.
Alternatively you could use a switch statement which since Java 7 should work with strings as well.
Try this:
public String execute(String headerValue) throws IllegalArgumentException, SecurityException, IllegalAccessException, NoSuchFieldException {
for(Field field:this.getClass().getFields()) {
if (field.isAnnotationPresent(Header.class)) {
Header annotation = field.getAnnotation(Header.class);
String name = annotation.value();
if(name.equals(headerValue)) {
Object val = this.getClass().getField(name).get(this);
return (String) val;
}
}
}
return null;
}
There are a couple of exception to handle in line:
Object val = this.getClass().getField(name).get(this);
You can return null for that exception if you don't want to throw it from this method.
This may help you
Field f[]= Parser.class.getDeclaredFields();
for (int i = 0; i < f.length; i++) {
Annotation annotation[]= f[i].getAnnotations();
for (int j=0;j<annotation.length;j++){
Class<Annotation> type = (Class<Annotation>) annotation[j].annotationType();
for (Method method : type.getDeclaredMethods()) {
if(method.getName() .equals(headerValue))
{
String name=f[i].getName();
return name;
}
}
}
}
Parser.class.getDeclaredFields() will include private fields also.

Handling meta of class (reference to properties)

I am trying to set up a small metamodel in order to reference some properties on multiple classes.
Example: Using the classes below, I'd like to store only Person.name and Person.surname in MetaManager.config. The problem is, I don't want to store the values of name and surname, but a reference to the field. By storing these references of the field, later on I can retrieve the name and surname of any instance of Person I would pass to MetaManager.getValues().
This code is similar to Metamodel API, though I am not sure whether I should use this (since Metamodel is part of persistence and this is not related to persistence). In this API the reference is made like this Person_.name using the EntityType object.
The question is, in what way can I store a reference to these properties so I can retrieve the value of these properties from an instance later on?
The code below gives a sketch of what I'm trying to accomplish. As you can see, my problem is in Person.getValue() and a toString() on this reference (a reference on ssn would thus return "ssn").
interface IMetable {
Object getValue(Meta meta);
}
class Person implements IMetable {
String ssn;
String name;
String surname;
Person(String ssn, String name, String surname) {
this.ssn = ssn;
this.name = name;
this.surname = surname;
}
#Override
Object getValue(ClassMeta meta) {
// Return the value of the (by meta) referenced field
return null;
}
}
class MetaManager {
Map<Class, Meta[]> config;
public Map<String, String> getValues(IMetable object) {
if(config.containsKey(object.class)) {
ClassMeta[] metamodel = config.get(object.class);
Map<String, String> values = new HashMap();
for(Meta meta : metamodel) {
values.put(meta.toString(), object.getValue(meta).toString());
}
return values;
}
else {
throw new Exception("This class has not been configurated.");
}
}
}
You appear to be trying to recreate the reflection API.
Why wouldn't you just implement MetaManager like this:
public class MetaManager
{
public Map<String, Object> getValues(Object object)
{
Map<String, Object> values = new HashMap<String, Object>();
for (Field field : object.getClass().getFields())
{
boolean wasAccessible = field.isAccessible();
try
{
field.setAccessible(true);
values.put(field.getName(), field.get(object));
}
finally
{
field.setAccessible(wasAccessible);
}
}
return values;
}
}
If you need a subset of fields then use an Annotation to mark those fields and then check for that Annotation before adding it to the values map.

Java Reflection - listing properties (getters & setters) of a class

public class foo
{
private String _name;
private String _bar;
public String getName() {
return _name;
}
public void setName(String name) {
_name = name;
}
public String getBar() {
return _bar;
}
public void setBarn(String bar) {
_bar = bar;
}
}
If I have the above class can I use reflection to list the properties defined by the getters and setters? I've tried the method below but it doesn't work, Field[] fields is left empty. I know I can do this in .Net but Java is a very different animal. Am I barking up the wrong tree altogether?
private HashMap<String, String> getHashMap(Object obj) {
HashMap<String, String> map = new HashMap<String, String>();
Class<?> cls = obj.getClass();
Field fields[] = cls.getFields();
for(Field f : fields) {
String name = f.getName();
String value = f.get(obj).toString();
map.put(name, value);
}
return map;
}
Also setters and getters maybe evil, should I just drop this?
Maybe use cls.getDeclaredFields instead ? (And f.setAccessible(true) before get private field).
If you want getter and setter you have to get method by getDeclaredMethods. Then I suggest using BeanUtils instead of writing your own reflection logic :) (IMHO less convenient is java.beans.Introspector).
Use the Introspector class. Obtain the BeanInfo and use getPropertyDescriptors() method. That should get you on the way.
You can do something like this:
List<Method> methods = Arrays.asList(getClass().getDeclaredMethods());
for (Method m : methods)
{
String name = m.getName();
if (name.startsWith("get") || name.startsWith("is"))
{
// Do something with the getter method
} else if (name.startsWith("set"))
{
// Do something with the setter method
}
}

Categories

Resources