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.
Related
I'm making a Java class where I can apply a filter (searchterms) on a List with DTO's. The filter looks like:
[{ field: "price", value: "10.0" }, { field: "name", value: "%phone%" }]
In my class I have the following method, to apply all the filters to the list:
public List<T> applyFilters(List<T> input, ArrayList<LinkedHashMap<String, String>> searchTerms) {
for (LinkedHashMap<String, String> searchTerm : searchTerms) {
input = input.stream()
.filter(row -> {
try {
return applySingleFilter(row, searchTerm);
} catch (Exception e) {
throw new CustomGraphQLException(400, "The filter field is not a valid field in this type");
}
})
.collect(Collectors.toList());
}
return input;
}
But the applySingleFilter has different implementations based on the type of a field. Like for Strings I create a Regex:
private boolean applySingleStringFilter (T category, LinkedHashMap<String, String> searchTerm) throws Exception {
String patternString = createCompareRegex(searchTerm.get("value"));
String propertyValue = (String) PropertyUtils.getProperty(category, searchTerm.get("field"));
return propertyValue.matches(patternString);
}
But for like a Float I want another comparison, I don't want to apply the Regex to a float. What is the best way to make sure the correct method is called based on the type of the field?
Well, you'd first need to know the type of the field and once you have that information you could maintain a repository of filters that apply to the type (some casting might be required though).
We had a similar system in place which in essence looked like this:
interface Filter<T> {
Class<T> getHandledType();
boolean apply(T element);
}
class DoubleFilter implements Filter<Double> {
public Class<Double> getHandledType() { return Double.class; }
boolean apply(Double element) {
//filter here
}
}
The repository was basically a Map<Class<?>, Filter<?>> and using it was like:
Object fieldValue = //get the field value;
Class<?> fieldType = fieldValue.getClass(); //ofc, check for null first
Filter<?> filter = repo.get(fieldType);
if( filter != null ) {
//nasty cast to a raw type to tell the compiler to allow the call
((Filter)filter).apply(fieldValue);
}
i have the following method
public static <E> APIGatewayProxyResponseEvent generateResponse(E request, E response, int statusCode){
JSONObject result = new JSONObject();
result.put(Constants.REQUEST, request);
result.put(Constants.RESPONSE, response);
return new APIGatewayProxyResponseEvent()
.withBody(result.toString())
.withStatusCode(statusCode)
.withHeaders(Constants.commonHeaders);
}
i am getting net.sf.json.JSONException: java.lang.reflect.InvocationTargetException when result.put(Constants.RESPONSE, response); is executed
response is
Also the corresponding class is:
public class PhysicalMediaURL extends MediaURL {
private static final String IDENTIFIER_PREFIX = "images/I/";
public PhysicalMediaURL(String physicalId, String extension, MediaHostnameProvider mediaHostnameProvider) {
super("images/I/" + physicalId, extension, mediaHostnameProvider);
}
}
public abstract class MediaURL implements URL {
private final String identifier;
private final String extension;
private final MediaHostnameProvider mediaHostnameProvider;
public MediaURL(String identifier, String extension, MediaHostnameProvider mediaHostnameProvider) {
this.identifier = identifier;
this.extension = extension;
this.mediaHostnameProvider = mediaHostnameProvider;
}
public String getIdentifier() {
return this.identifier;
}
public String getExtension() {
return this.extension;
}
public String getDomainName() {
return this.mediaHostnameProvider.getMediaHostname(this.getExtension());
}
public String getURL() {
StringBuilder urlBuilder = new StringBuilder();
urlBuilder.append("https://");
urlBuilder.append(this.getDomainName());
urlBuilder.append('/');
urlBuilder.append(this.getIdentifier());
urlBuilder.append('.');
urlBuilder.append(this.getExtension());
return urlBuilder.toString();
}
public List<String> getStyleTags() {
return null;
}
}
where PhysicalMediaURL is of type: URL and that is an interface
public interface URL {
String getIdentifier();
String getDomainName();
String getExtension();
List<String> getStyleTags();
String getURL();
}
I am a bit stuck in this.. need help.
First off it looks like you are using a JSON implementation that is not updated as regularly as the other ones(Your exception is from net.sf.json). I always recommend using the org.json implementation as it receives regular updates and bugfixes.
Most implementations of JSONObject, when used in this form, use bean based reflection to retrieve values from your object. This is not always what you want when your object is in an inheritance hierarchy because, depending on the object and the JSONObject impl, it will pull fields from the implementation that are not on your higher level type(URL in this case).
If you really want a generic serialization function use something like Jackson or Gson that will allow you to specify the type as a part of the serialization. Otherwise consider transforming your objects, before they are passed to your generateResponse function, into simpler objects such as a Map<String, String> that can serialize unambiguously.
As a final thought JSONObject's generic serialization works, but, its performance is likely to be worse than using a dedicated higher level serializer like Jackson. It's best used with the explicit put methods to generate simple objects.
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");
In my Android app I have json, which looks like :
{
"Records": [
{
"RowIndex": "0",
"NameValue": {
"Name": "PropertyName1",
"Value": "PropertyValue1"
}
}{
"RowIndex": "1",
"NameValue": {
"Name": "PropertyName2",
"Value": "PropertyValue2"
}
}
]
}
I need to parce this json to object, which looks like:
public class MyClass {
public String PropertyName1;
public String PropertyName2;
}
And result after parsing should be:
public String PropertyName1 = "PropertyValue1";
public String PropertyName2 = "PropertyValue2";
Basically, the first json is equivalent of:
{
"PropertyName1" : "PropertyValue1",
"PropertyName2" : "PropertyValue2"
}
Question: How can I parce first json without usage swith/case to search for the necessary Property?
You'll have to go down the dark path of reflection I'm afraid.
you can parse the json into an intermediary object which has a map for namevalue.
then you use the below code (ofcourse just copy paste the bits you need) to loop over the map of key/value pairs. for each key look up the field you want, and set it. If you're guaranteed only to need to set public variables then you can use getFields and can skip the setAccessible.
public class Test {
public static void main(String[] argv) {
MyClass myClass = new MyClass();
Class<?> classObject = myClass.getClass();
// Field fields[] = classObject.getFields(); // if you want to get only public fields.
Field fields[] = classObject.getDeclaredFields(); // any field
for(Field f : fields) {
System.out.println(f.getName());
try {
// if member is private: security managers may object but the default java allows it
f.setAccessible(true);
f.set(myClass, "abc");
} catch (IllegalAccessException e) {
// handle access exception:
e.printStackTrace();
}
}
System.out.println("prop 1: " + myClass.PropertyName1);
System.out.println("prop 2: " + myClass.PropertyName2);
}
public static class MyClass {
public String PropertyName1;
private String PropertyName2;
}
}
Actually.. there is a non-reflect way but that will replace your implementation of the object you have.
If you change your class:
public class MyClass {
public String PropertyName1;
public String PropertyName2;
}
to
public class MyClass {
private Map<String, String> properties = new HashMap<String, String>();
public void setProperties(Map<String, String> props) { this.properties = props; }
public String getPropertyName1() {
return lookupProperty("PropertyName1");
}
public String getPropertyName2() {
return lookupProperty("PropertyName2");
}
private String lookupProperty(String property) {
if (properties.containsKey(property) {
return properties.get(property);
} else {
return null;
}
}
}
then you could parse the name value map into a map, and construct a myclass with it.
just listing it for completeness, though changing your domain model to fit a json input is not ideal.
I would recommend either way to do the input parsing, and then copy over the model into your actual domain object rather than using the json-model in your application. that way if the json model ever changes, your domain model will not change.
One method I can think of (which doesn't sound too great) is to actually make an object that matches the JSON response you get back. Then, map THAT NameValue object to MyClass
So, something like
public class NameValue {
public string Name;
public String Value;
public MyClass getMyClass(){
MyClass myClass = new MyClass();
myClass.PropertyName2 = Value;
return myClass;
}
}
You can come up with a better way to map it, obviously. But this is just an example of something I might do if I was given a response JSON I didn't particularly care for. You can similarly reverse it (have MyClass be able to create a NameValue object) so you can send data back in the correct format.
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
}
}