I have a method:
public void extractStringFromField(Class<?> classToInspect) {
Field[] allFields = classToInspect.getDeclaredFields();
for(Field field : allFields) {
if(field.getType().isAssignableFrom(String.class)) {
System.out.println("Field name: " + field.getName());
// How to get the actual value of the string?!?!
// String strValue = ???
}
}
}
When this runs I get output like:
Field name: java.lang.String
Now how do I extract the actual string value into strValue, using reflection?
It looks like you need a reference to an instance of the class. You would want to call get and pass in the reference, casting the return to a String.
You can use get as follows:
String strValue = (String) field.get (objectReference);
In ideal situations,Class does not hold data. It merely holds the information about the structure and behavior of its instances and Instances of the Classes hold your data to use. So your extractStringFromField method can not extract values unless you pass any instances (from where it will actually extract values).
If the name of the parameter of the reference, you are passing to extract value is instance, then you can easily get what you want like bellow:
String strValue = (String)field.get(instance);
Just usefull example code for reflection fields:
Field[] fields = InsanceName.getDeclaredFields();
for (Field field : fields) { //array for fields names
System.out.println("Fields: " + Modifier.toString(field.getModifiers())); // modyfiers
System.out.println("Fields: " + field.getType().getName()); //type var name
System.out.println("Fields: " + field.getName()); //real var name
field.setAccessible(true); //var readable
System.out.println("Fields: " + field.get(InsanceName)); //get var values
System.out.println("Fields: " + field.toString()); //get "String" values
System.out.println(""); //some space for readable code
}
Just had the same issue. This Thread somewhat helped. Just for reference if somebody else stumbles upon this thread. I used the StringBuilder class to convert so basically:
StringBuilder builder = new StringBuilder();
builder.append(field.get(object))
Which has multiple advantages. One that you do not explicitly cast (which btw. causes problems with primitive types int vs. Integer) but also of being more efficient if you have multiple string operations sequentialy. In time critical code parts.
String strValue = field.getName().toString();
Full code looks like this:
public static void extractStringFromField(Class<?> Login) {
Field[] allFields = Login.getDeclaredFields();
for(Field field : allFields) {
String strValue = field.getName().toString();
// if(field.getType().isAssignableFrom(String.class)) {
System.out.println("Field name: " + strValue);
}
}
Related
Let's say I have a method called Object classInstance = createInstance("Page", map, factory) which creates an instance of "Page" with java reflection and does some stuff with it. For demonstration purposes I've called it "Page", but it could be any of my classes.
Now I want to add this object to a List<Page>. To call list.add(classInstance) and add it to the list I need to parse it to "Page". Is there a way to do it, given the only information I have is the string containing the class name? So instead of doing (Page) classInstance I need to do something like (Class.forName("Page")) classInstance.
I can not modify the List or the way it is added to the list.
Thank you.
Edit: here is the createInstance Method:
private static Object createInstance(String className, Map<?, ?> map, Meilenstein2Factory factory) throws InvocationTargetException, IllegalAccessException {
try {
String createMethodName = "create" + className;
Method createMethod = factory.getClass().getMethod(createMethodName);
Object classInstance = createMethod.invoke(factory);
String methodName = "";
for (Map.Entry<?, ?> entry : map.entrySet()) {
try {
methodName = "set" + entry.getKey().toString().substring(0,1).toUpperCase() + entry.getKey().toString().substring(1);
Method setNameMethod = classInstance.getClass().getMethod(methodName, getType(entry.getValue()));
setNameMethod.invoke(classInstance, parseEntry(entry.getValue()));
} catch (NoSuchMethodException e) {
LOGGER.log(null, "Attribute " + entry.getKey().toString() + " is not a valid attribute for this object. Is it spelled correctly?");
}
}
return classInstance;
} catch(NoSuchMethodException nm) {
LOGGER.log(null, "Folder " + className + " does not reference to a valid object. Is it spelled correctly?");
}
return null;
}
Edit 2: Screenshots of error and debugger
Do not bother about Page and PageImpl, I used Page in my question to simplify, but the factory accepts the Interface Page and returns an instance of PageImpl. As you can see in the second screenshot, the object is an instance of PageImpl, so this seems to be correct.
Edit 3:
Edit 4:
Something that works for now:
String methodName = "get" + "Page";
Method getListMethod = site.getClass().getMethod(methodName);
List<Object> list = (List<Object>) getListMethod.invoke(site);
list.add(page);
Your method createInstance returns a Class<Page> (the class object), but your list is a List<Page> (a list of instances).
You will need to create an instance and add it to the list, still using reflection:
list.add(classInstance.getDeclaredConstructor().newInstance());
The above is using an empty constructor for the class Page. If for example you wanted to use a constructor taking one string and one int, you would do it as such:
list.add(classInstance.getDeclaredConstructor(String.class, Integer.class).newInstance("my string", 4));
Edit:. Since classInstance is an Object and not a class, you should be able to do this:
list.add(Class.forName("Page").cast(classInstance));
I have an array of variable;
String[] variableArray = {"id","name","address"};
Each element of the array are variable names of a class Person.
so to get the value we can use
Person::getId;
Person::getName;
Person::getAddress;
Is there any way where i can iterate the array and get the values using method reference..?
Arrays.asList(variableArray).forEach(objName -> {
Person::get 'objName'
});
You have to use reflection.
Try (If you do it in your way):
var clazz=Person.class;
Person thisReference=...;
Arrays.asList(variableArray).forEach(objName -> {
String cap = objName.substring(0, 1).toUpperCase() + objName.substring(1);
String property=null;
try{
var method=clazz.getDeclaredMethod(getDeclaredMethod);
property=(String)method.invoke(thisReference);
} catch(Exception exception){
//Do something (and maybe split up the exceptions to handle each one different)
}
//Do something with property.
});
I would do it in this way:
var clazz=Person.class;
Person thisReference=...;
//You will have to do error handling.
Method[] methodArray=new Method[]{clazz.getDeclaredMethod("getId"),
clazz.getDeclaredMethod("getName"),
clazz.getDeclaredMethod("getAddress")};
for(Method method:methodArray){
//Again, error handling...
String property = (String) method.invoke(thisReference);
}
You can certainly use reflection yourself or use a library that encapsulates the reflection part like BeanUtils. If you have a fixed set of properties only and don't aim for a generic solution using a simple mapping might work as well:
Person person = new Person(); // you need an instance of person as the getters are not static
String[] variableArray = {"id","name","address"};
Map<String, Supplier<String>> getters = new HashMap<>();
getters.put("id", person::getId);
getters.put("name", person::getName);
getters.put("address", person::getAddress);
Arrays.asList(variableArray).forEach(objName -> {
String value = getters.get(objName).get();
// do something with value
});
As the getters in Person are however not static you will need to have the actual person instance for the method references to work.
I have a model class in java and I overwrote the toString to provide me with a custom toString. The toString uses reflection to find the field names and values and it works when I run it locally via my ide. However when I run it via mvn agents I always seem to get the error:
java.lang.ClassCastException: [Z cannot be cast to [Ljava.lang.String
Here is the toString:
#SneakyThrows
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
Class<?> thisClass = Class.forName(this.getClass().getName());
Field[] aClassFields = thisClass.getDeclaredFields();
for (Field f : aClassFields) {
String fName = f.getName();
fName = fName.startsWith("_") ? fName.substring(1) : fName;
if (null != f.get(this)) {
if (f.get(this) instanceof String || f.get(this) instanceof List) {
sb.append(getVariableNameStr(fName, f.get(this).toString()));
} else {
StringBuilder stringArrayStr = new StringBuilder();
for (String s : (String[]) f.get(this)) {
stringArrayStr.append(fName).append(": ").append(s).append(", ");
}
sb.append(stringArrayStr);
}
}
}
return sb.toString().substring(0, sb.toString().length() - 2);
}
The line it fails on in the code is the following:
for (String s : (String[]) f.get(this)) {
Why does this pass locally and fail using mvn?
Can anybody tell me what is incorrect about this line?
Just to clear up - the model class has 3 types of field - String, List and String array. The errored line occurs on String array entries.
A
I would assume its caused by some other libraries or test suite touching your code. Or some difference in configuration between local run and maven might cause your objects to be wrapped in some other proxy classes, as proxy classes are often used by frameworks like spring.
You should filter out fields that are not part of object, by removing static and synthetic fields. As like I said in comment, [Z is a boolean[] field, so for sure you are getting some extra fields here.
Also reflections are already bad for performance, and you are repeating field.get call multiple times for no reason. And I don't understand at all why you are assuming field to be of String[] type without checking it.
public String toString() {
StringBuilder sb = new StringBuilder();
Class<?> thisClass = this.getClass();
Field[] aClassFields = thisClass.getDeclaredFields();
for (Field f : aClassFields) {
//skip static and synthetic fields:
if (f.isSynthetic() || Modifier.isStatic(f.getModifiers())) continue;
// get value only once:
Object value = f.get(this);
String fName = f.getName();
fName = fName.startsWith("_") ? fName.substring(1) : fName;
if (value != null) { // and use this value here
if (value instanceOf String[]) {
StringBuilder stringArrayStr = new StringBuilder();
for (String s : (String[]) value) { // and here
stringArrayStr.append(fName).append(": ").append(s).append(", ");
}
sb.append(stringArrayStr);
} else {
sb.append(getVariableNameStr(fName, value.toString()));
}
}
}
return sb.toString().substring(0, sb.toString().length() - 2);
}
I also reordered ifs to handle String[] case first, and use the simple generic toString for rest of possible objects, as this seems to be your case.
Also it would be much better solution to just generate normal toString method or use some libraries like ToStringBuilder from apache commons. As it does not look like you need to use reflections here at all.
[Z means boolean[] - not String[]. So the field is of type boolean[].
I am trying to display all the strings used in a method invocation using SOOT program analysis framework. I am able to check for StringConstant but how do I get values for RefType ? Here is the sample code :
for (Value va : iv.getInvokeExpr().getArgs()) {
System.out.println("[ARGS : TYPE] " + va.getType() + " with ");
if (va instanceof StringConstant) {
System.out.print(va + " ");
} else if (va instanceof JimpleLocal) {
JimpleLocal jl = (JimpleLocal) va;
if (jl.getType() instanceof RefType) {
RefType rt = (RefType) jl.getType();
SootClass cls = rt.getSootClass();
String clsName = cls.getName();
// recursion possible - backward analysis ?
if(clsName.equals("java.lang.String")){
System.out.print("GOT STRING CLASS - HOW TO GET THE VALUE ?");
}
}
}
}
I am new to the program analysis domain, any pointers will be of great help.
Thanks
StringConstant had a getValue Methode. Just cast The value to this type. For locals your questions does not make sense, as they are variables, not constants.
I am currently implementing an Annotation that forces the fields to respect a condition through javassist. I would like to check if a field is initialized when it is being read... so, currently, I am getting the classes by loading them when they are loaded by the VM through a Translator.onLoad(ClassPool pool, String className), and using an ExprEditor on each class through overriding the edit(FieldAccess arg) method. Right now, I managed to inject code to check the condition by running the following method inside onLoad :
private void processFields(FieldsAndMethods data) {
final FieldsAndMethods copy = data;
Stack<CtClass> classes = data.getThisClass();
for(CtClass cc : classes ){
try {
cc.instrument(new ExprEditor(){
#Override
public void edit(FieldAccess arg) throws CannotCompileException{
try{
CtField field = arg.getField();
if(copy.getFields().contains(field) &&
field.hasAnnotation(Assertion.class)){
Assertion a =
((Assertion)field.getAnnotation(Assertion.class))
String condition = assertion.value();
String fieldName = field.getName();
String processCondition =
transformCondition(condition, fieldName);
if(arg.isWriter()){
String code = "{if(" + evaledCondition + ")" +
"$proceed($$) ;" +
"else throw new " +
"RuntimeException(\"The assertion " +
condition + " is false.\");}";
arg.replace(code);
}else if (arg.isReader()){
//Here is where I would like to check if the field
//has been initialized...
}
}catch(ClassNotFoundException e){
System.out.println("could not find Annotation " +
Assertion.class.getName() );
}catch(NotFoundException e){
System.out.println("could not find field " +
arg.getFieldName() );
}
}
});
} catch (CannotCompileException e) {
System.out.println("Could not interpret the expression");
System.out.println(e);
}
}
}
private String transformCondition(String condition, String fieldName){
return condition.replace(fieldName, "$1");
}
Could you point me in the right direction for finding out if a field has been initialized? Notice that a field can be either a primitive or not.
Thanks in advance.
Assumptions
I'll assume the following:
By field initialized we are talking about fields that are null.
Primitive types cannot be null so no bother to check them.
The code
This example verification will work for both static and non static fields.
I've also created the code String in several lines for better readability. Being arg a FieldAccess object, you can write the following:
if (arg.isReader() && !arg.getField().getType().isPrimitive()) {
String code = "{ java.lang.Object var = $proceed();"
+ "if(var == null) {"
+ "java.lang.System.out.println(\"not initialized " + arg.getFieldName() + "\");"
+ "}"
+ "$_=var;}";
arg.replace(code);
}
Code Explanation
As you can see, in this small example I've used a few javassist identifiers, for the complete reference about this please read the javassist official tutorial (I'm linking to the section about code modifications).
Here is what each identifier used means:
$proceed() : in the case of a field access this returns the value of the field.
$_ : this is an identifier that is mandatory when editing a FieldAccess in read mode. This token holds the value that will be used to set the field.
With this information it's easy to understand the code's idea:
Put the field value into an auxiliary object named var
Check if the field is null, if so print a warning with the field name
Set the fieldname with the value (either it's null or not);
I guess this already points you to the right direction. But let me know if you need anything else.