I am try to use reflect to create some instance through the json configuration:
config.json:
{
"clazz":com.kk.Manager",
"args":{
"path":"/home/kk/a.txt",
"scale":1
}
}
Implementation:
packge com.kk;
public class Manager{
private File file;
private int scale;
public Manager(String path,int scale){
this.file = new File(path)
this.scale=scale;
//other initialization work omitted
}
}
private static Object buildObject(String clazz, JSONObject args) {
Object instance;
try {
Class clas = Class.forName(clazz);
if (args == null) {
instance = clas.newInstance();
} else {
Class[] pamas = new Class[args.length()];
Object[] vals = new Object[args.length()];
Iterator<String> parameterIt = args.keys();
int i = 0;
while (parameterIt.hasNext()) {
String fd = parameterIt.next();
Object val = args.get(fd);
pamas[i] = val.getClass();
vals[i] = val;
i++;
}
instance = clas.getConstructor(pamas).newInstance(vals);
}
return instance;
} catch (Exception e) {
return null;
}
return null;
}
After I parse the json config file, I will call it like this:
String clazz = parent.getString("clazz");
JSONObject args = parent.getJSONObject("args")
buildObject(clazz,args);
While the code throw error:
java.lang.NoSuchMethodException: com.kk.Manager.<init>(java.lang.Integer, java.lang.String)
It seem that the constructor of Manager is called with parameters in wrong order.
After google, it seems that the order of keys in a JSONObject is undefined.
So I wonder if I can get the constructor by the parameter name, something like:
Class cls = Class.forName(clazz);
cls.getConstructor("path","scale");
Or if not, how can I fix this without add the setter method?
Related
I want to keep a part of a JSON as String value.
As far as i know, there is no way with Annotations, but i could not find a way how to get the full Object/Array value as String.
There is a Workaround, which works, by reading it as an Object and instantly write it back as an String by using the ObjectMapper of Jackson.
You can imagine, this is a horrible solution for very big JSONs.
public class DeserializeTest {
private static ObjectMapper mapper;
public static void main(String[] args) throws IOException {
mapper = Jackson2ObjectMapperBuilder.json().build();
mapper.findAndRegisterModules();
SimpleModule module = new SimpleModule();
module.addDeserializer(TestClassWrapper.class, new TestDeserializer());
mapper.registerModule(module);
String json = "{\"name\":\"testprop\", \"data\":[{\"prop\":\"test\"},{\"prop\":\"test1\"},{\"prop\":\"test2\"}]}";
TestClassWrapper t = mapper.readValue(json, TestClassWrapper.class);
// later in program, when i know the expected class
TestClass o = unwrap(t, new TypeReference<ArrayList<Test2>>() {});
}
public static class TestClassWrapper {
String name;
String data;
// removed getter and setter
}
public static class TestClass {
String name;
List<Test2> data;
// removed getter and setter
}
public static class Test2 {
String prop;
// removed getter and setter
}
public static class TestDeserializer extends JsonDeserializer<TestClassWrapper> {
#Override
public TestClassWrapper deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
TestClassWrapper t = new TestClassWrapper();
String key = p.getCurrentName();
if (key == null) {
p.nextToken();
key = p.getCurrentName();
}
for (; key != null; key = p.nextFieldName()) {
p.nextToken();
switch (key) {
case "name":
t.name = p.getValueAsString();
break;
case "data":
// what i tried:
System.out.println(p.getText()); // [
System.out.println(p.getValueAsString()); // NULL
System.out.println(p.getCurrentValue()); //NULL
System.out.println(p.getCurrentToken()); // [ TOKEN
System.out.println(p.getParsingContext().getCurrentValue()); // NULL
System.out.println(p.getParsingContext().toString()); // [0]
System.out.println(p.getEmbeddedObject()); // NULL
System.out.println(p.getTextCharacters()); // [
try {
System.out.println(ctxt.readValue(p, String.class)); // MismatchedInputException
} catch (MismatchedInputException e){}
// The only way i could make it work.
// Parse to a object and write it back as string.
StringBuilder sb = new StringBuilder();
Iterator<Object> it = p.readValuesAs(Object.class);
while (it.hasNext()) {
sb.append(mapper.writeValueAsString(it.next()));
sb.append(it.hasNext() ? "," : "");
}
t.data = p.getCurrentToken() == JsonToken.END_ARRAY ? "[" + sb.toString() + "]" : sb.toString();
break;
}
}
return t;
}
}
public static TestClass unwrap(TestClassWrapper t, TypeReference targetClass) throws IOException {
TestClass o = new TestClass();
o.name = t.name;
o.data = mapper.readValue(t.data, targetClass);
return o;
}
}
How can i tell the JsonParser object, to just give me the String of the current value?
(For data this would be: "[{"prop":"test"}, {"prop":"test1"}, {"prop":"test2"}]")
I have requirement where I need to convert java object to json.
I am using Gson for that but i need the converter to only serialize the non null or not empty values.
For example:
//my java object looks like
class TestObject{
String test1;
String test2;
OtherObject otherObject = new OtherObject();
}
now my Gson instance to convert this object to json looks like
Gson gson = new Gson();
TestObject obj = new TestObject();
obj.test1 = "test1";
obj.test2 = "";
String jsonStr = gson.toJson(obj);
println jsonStr;
In the above print, the result is
{"test1":"test1", "test2":"", "otherObject":{}}
Here i just wanted the result to be
{"test1":"test1"}
Since the test2 is empty and otherObject is empty, i don't want them to be serialized to json data.
Btw, I am using Groovy/Grails so if there is any plugin for this that would be good, if not any suggestion to customize the gson serialization class would be good.
Create your own TypeAdapter
public class MyTypeAdapter extends TypeAdapter<TestObject>() {
#Override
public void write(JsonWriter out, TestObject value) throws IOException {
out.beginObject();
if (!Strings.isNullOrEmpty(value.test1)) {
out.name("test1");
out.value(value.test1);
}
if (!Strings.isNullOrEmpty(value.test2)) {
out.name("test2");
out.value(value.test1);
}
/* similar check for otherObject */
out.endObject();
}
#Override
public TestObject read(JsonReader in) throws IOException {
// do something similar, but the other way around
}
}
You can then register it with Gson.
Gson gson = new GsonBuilder().registerTypeAdapter(TestObject.class, new MyTypeAdapter()).create();
TestObject obj = new TestObject();
obj.test1 = "test1";
obj.test2 = "";
System.out.println(gson.toJson(obj));
produces
{"test1":"test1"}
The GsonBuilder class has a bunch of methods to create your own serialization/deserialization strategies, register type adapters, and set other parameters.
Strings is a Guava class. You can do your own check if you don't want that dependency.
What I personally don't like in TypeAdapter using answer is the fact you need to describe every field of your entire class which could have lets say 50 fields (which means 50 if blocks in TypeAdapter).
My solution is based on Reflection and a fact Gson will not serialize null values fields by default.
I have a special class which holds data for API to create document called DocumentModel, which has about 50 fields and I don't like to send String fields with "" (empty but not null) values or empty arrays to server. So I created a special method which returns me a copy of my object with all empty fields nulled. Note - by default all arrays in my DocumentModel instance are initialized as empty (zero length) arrays and thus they are never null, you should probably check your arrays for null before checking their length.
public DocumentModel getSerializableCopy() {
Field fields[] = new Field[]{};
try {
// returns the array of Field objects representing the public fields
fields = DocumentModel.class.getDeclaredFields();
} catch (Exception e) {
e.printStackTrace();
}
DocumentModel copy = new DocumentModel();
Object value;
for (Field field : fields) {
try {
value = field.get(this);
if (value instanceof String && TextUtils.isEmpty((String) value)) {
field.set(copy, null);
// note: here array is not being checked for null!
else if (value instanceof Object[] && ((Object[]) value).length == 0) {
field.set(copy, null);
} else
field.set(copy, value);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return copy;
}
Using this method I don't care if some fields was added or removed after this method was written or whatever. The only problem left - is checking custom type fields, which are not String or array, but this depends to particular class and should be extra coded in if/else blocks.
It seems to me the problem is not with gson. Gson correctly keeps track of the difference between null and an empty string. Are you sure you want to erase that distinction? Are you sure all classes that use TestObject don't care?
What you could do if you don't care about the difference is to change the empty strings to null within a TestObject before serializing it. Or better, make the setters in TestObject such that an empty string is set to null; that way you define rigidly within the class that an empty string is the same as null. You'll have to make sure the values cannot be set outside the setters.
I have ran into the same problem and found 2 distinct solutions
Write a custom TypeAdapter for each field class
TypeAdapter example for String class:
#SuppressWarnings("rawtypes")
public class JSONStringAdapter extends TypeAdapter {
#Override
public String read(JsonReader jsonReader) throws IOException {
String value = jsonReader.nextString();
if(value == null || value.trim().length() == 0) {
return null;
} else {
return value;
}
}
#Override
public void write(JsonWriter jsonWriter, Object object) throws IOException {
String value = String.valueOf(object);
if(value == null || value.trim().length() == 0) {
jsonWriter.nullValue();
} else {
jsonWriter.value(value);
}
}
}
Use:
public class Doggo {
#JsonAdapter(JSONStringAdapter.class)
private String name;
public Doggo(String name) {
this.name = name;
}
}
public class Main {
public static void main(String[] args) {
Doggo aDoggo = new Doggo("");
String jsonString = new Gson().toJson(aDoggo);
}
}
Process the object manually before generating the JSON string
Seems to work on anything, haven't tested the performance:
public static boolean removeEmpty(JSONObject source) {
if (null == source || source.length() == 0) {
return true;
}
boolean isJsonObjectEmpty = false;
for (String key : JSONObject.getNames(source)) {
Object value = source.get(key);
boolean isValueEmpty = isValueEmpty(value);
if(isValueEmpty) {
source.remove(key);
}
}
if(source.length() == 0) {
isJsonObjectEmpty = true;
}
return isJsonObjectEmpty;
}
private static boolean isValueEmpty(Object value) {
if (null == value) {
return true;
}
if (value instanceof JSONArray) {
JSONArray arr = (JSONArray) value;
if(arr.length() > 0) {
List<Integer> indextesToRemove = new ArrayList<>();
for(int i = 0; i< arr.length(); i++) {
boolean isValueEmpty = isValueEmpty(arr.get(i));
if(isValueEmpty) {
indextesToRemove.add(i);
};
}
for(Integer index : indextesToRemove) {
arr.remove(index);
}
if(arr.length() == 0) {
return true;
}
} else {
return true;
}
} else if (value instanceof JSONObject) {
return removeEmpty((JSONObject) value);
} else {
if (JSONObject.NULL.equals(value)
|| null == value
|| value.toString().trim().length() == 0)
) {
return true;
}
}
return false;
}
Use:
public class Doggo {
private String name;
public Doggo(String name) {
this.name = name;
}
}
public class Main {
public static void main(String[] args) {
Doggo aDoggo = new Doggo("");
// if you are not using Type Adapters for your fields
JSONObject aJSONObject1 = new JSONObject(aDoggo);
removeEmpty(aJSONObject1);
String jsonString1 = aJSONObject1.toString();
// if you are using Type Adapters for your fields
Gson gsonParser = new Gson();
JSONObject aJSONObject2 = new JSONObject(gsonParser .toJson(aDoggo));
removeEmpty(aJSONObject2);
String jsonString2 = aJSONObject2.toString();
}
}
I want to make a Map (String ,Object) like this
{AssessmentId=0, Physical_name='ram', Physical_height=20, Physical_weight=60}
from my Pojo Class - InitialAssessment
public class InitialAssessment {
private long AssessmentId;
private String physical_name;
private String physical_gender;
private int physical_height;
private float physical_weight;
// all getter And setter is Created here
}
without using any external Library like Gson etc.
You can use this approach:
public Map getMapFromPojo(InitialAssessment assessment) throws Exception {
Map<String, Object> map = new HashMap<>();
if (assessment != null) {
Method[] methods = assessment.getClass().getMethods();
for (Method method : methods) {
String name = method.getName();
if (name.startsWith("get") && !name.equalsIgnoreCase("getClass")) {
Object value = "";
try {
value = method.invoke(assessment);
map.put(name.substring(name.indexOf("get") + 3), value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return map;
}
return null;
}
It will give you map for pojo class like this:
Output:
{AssessmentId=0, Physical_name='ram', Physical_gender='Male' , Physical_height=20, Physical_weight=60}
Hi i am using reflections to achieve something.
I have been given class name, method name of that class and parameter values that needs to be passed to that method in a file(Take any file. Not a constraint).
I have to call that method with the parameters. This methods do not return anything.
There is a huge list of methods in this classes and parameter list of each varies.
E.g: method1(String, String, int, boolean)
method1(String, int, boolean) and likewise i have different permutations and combinations.
So how can i achieve this.
I have tried hard coding things with different switch clauses but it is a real overhead and risky thing to maintain.
Can we dynamically do this thing, like on the fly read the method name and its parameter from the file and call it.
Any small code snippet will be helpful.
TIA.
Hi all i have found the solution to the above question. below is the sample code snippet.
package reflections;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionTest {
public void method1(String str, int number) {
System.out.println(str + number);
}
public void method1(String str) {
System.out.println(str);
}
public void method1() {
System.out.println("helloworld");
}
public static void main(String[] args) throws ClassNotFoundException,
InstantiationException, IllegalAccessException,
NoSuchMethodException, SecurityException, IllegalArgumentException,
InvocationTargetException {
// Step 1) Make an object array and store the parameters that you wish
// to pass it.
Object[] obj = {};// for method1()
// Object[] obj={"hello"}; for method1(String str)
// Object[] obj={"hello",1}; for method1(String str,int number)
// Step 2) Create a class array which will hold the signature of the
// method being called.
Class<?> params[] = new Class[obj.length];
for (int i = 0; i < obj.length; i++) {
if (obj[i] instanceof Integer) {
params[i] = Integer.TYPE;
} else if (obj[i] instanceof String) {
params[i] = String.class;
}
// you can do additional checks for other data types if you want.
}
String methoName = "method1"; // methodname to be invoked
String className = "reflections.ReflectionTest";// Class name
Class<?> cls = Class.forName(className);
Object _instance = cls.newInstance();
Method myMethod = cls.getDeclaredMethod(methoName, params);
myMethod.invoke(_instance, obj);
}
}
I hope this will help others too.
public class ReflectionSample
{
private Object mString = null;
private int mValue;
public ReflectionSample()
{
}
public ReflectionSample(int oValue)
{
mValue = oValue;
}
public ReflectionSample(String oString)
{
mString = oString;
}
public ReflectionSample(String oString, int oValue)
{
setValues(oString, oValue);
}
public void setValues(String oString, int oValue)
{
mString = oString;
mValue = oValue;
}
public String toString()
{
return ""+mString+":"+mValue;
}
public void run()
{
String oInput = "Teststring";
Class<?> cls;
String clsname = "main.ReflectionSample";
Object rs = null; // ReflectionSample
Object rsc = null;
System.out.println(this.getClass().getName());
try
{
System.out.println(clsname);
cls = Class.forName(clsname);
if(cls == null)
{
System.err.println(clsname + " doesn't exist");
return;
}
// Look for a constructor which has a single string
Constructor<?> ct = null;
Class<?>[] param_types = new Class<?>[1];
Object[] arguments = new Object[1];
param_types[0] = String.class;
// get the string constructor
ct = cls.getConstructor(param_types);
// We only have one object
arguments = new Object[1];
arguments[0] = oInput;
// Instantiate the object with passed in argument.
rs = ct.newInstance(arguments);
System.out.println("String constructor sample: "+rs);
// Instantiate with default constructor
param_types = new Class<?>[0];
arguments = new Object[0];
ct = cls.getConstructor(param_types);
rs = ct.newInstance(arguments);
rsc = rs; // Keep it for later, to lazy to call it again
System.out.println("Default constructor sample: "+rs);
// Instantiate with string and int constructor
param_types = new Class<?>[2];
arguments = new Object[2];
// Must be in the same order as the params I think
param_types[0] = String.class;
param_types[1] = Integer.TYPE; // <-- Its a primitive so use TYPE not Class
arguments[0] = oInput;
arguments[1] = new Integer(1);
ct = cls.getConstructor(param_types);
rs = ct.newInstance(arguments);
System.out.println("String plus int constructor sample: "+rs);
// call the setValues method
param_types[0] = String.class;
param_types[1] = Integer.TYPE; // <-- Its a primitive so use TYPE not Class
arguments[0] = oInput;
arguments[1] = 1;
System.out.println("setValues invocation before: "+rsc);
Method m = cls.getMethod("setValues", param_types);
m.invoke(rsc, arguments);
System.out.println("setValues invocation after: "+rsc);
// An alternative method to pass the parameters
m = cls.getMethod("setValues", String.class, Integer.TYPE);
m.invoke(rsc, oInput+"x", 2);
System.out.println("setValues invocation after: "+rsc);
}
catch(Throwable e)
{
System.err.println(e.getLocalizedMessage());
}
}
}
Output:
main.ReflectionSample
main.ReflectionSample
String constructor sample: Teststring:0
Default constructor sample: null:0
String plus int constructor sample: Teststring:1
setValues invocation before: null:0
setValues invocation after: Teststring:1
Hope this helps.
I don't know if this is a newer feature in Java, but I have seen that you can use invoke now with parameters as well, instead of using an array, which might make your code better to read (This is the alternative way). If you need a variable number of arguments and you don't know beforehand how many there will be, allocating the array is defeinitly working and should also be backwardcompatible.
A simple solution would be to create a Class with the Arguments required to be passed:
public class ObjectArguments {
private PrintWriter out;
private String productId;
private int action;
public ObjectArguments(PrintWriter out, String productId, int action) {
this.out = out;
this.productId = productId;
this.action = action;
}
public PrintWriter getOut() {
return out;
}
public String getProductId() {
return productId;
}
public int getAction() {
return action;
}
}
Assuming that you want to invoke a class Foo with a method named bar.
Then it would be done like this.
PrintWriter out = null;
String productId = null;
int action = 0;
Class[] paramArguments = new Class[1];
paramArguments[0] = ObjectArguments.class;
ObjectArguments newObj = new ObjectArguments(out,productId,action);
Class cls = Class.forName("Foo");
Object obj = cls.newInstance();
Method method = cls.getDeclaredMethod("bar", paramArguments);
method.invoke(obj, newObj);
For two int parameters the example is as below, similarly other datatype parameters can also be called
Method method=new Test1().getClass().getMethod(x, new Class[] {int.class,int.class});
We can call a method that needs 3 arguments int,int,string as below :
Method method=new Test1().getClass().getMethod(x, new Class[] {int.class,int.class, String.class});
TLDR: I'd like to know how to extend fit.TypeAdaptor so that I can invoke a method that expects parameters as default implementation of TypeAdaptor invokes the binded (bound ?) method by reflection and assumes it's a no-param method...
Longer version -
I'm using fit to build a test harness for my system (a service that returns a sorted list of custom objects). In order to verify the system, I thought I'd use fit.RowFixture to assert attributes of the list items.
Since RowFixture expects the data to be either a public attribute or a public method, I thought of using a wrapper over my custom object (say InstanceWrapper) - I also tried to implement the suggestion given in this previous thread about formatting data in RowFixture.
The trouble is that my custom object has around 41 attributes and I'd like to provide testers with the option of choosing which attributes they want to verify in this RowFixture. Plus, unless I dynamically add fields/methods to my InstanceWrapper class, how will RowFixture invoke either of my getters since both expect the attribute name to be passed as a param (code copied below) ?
I extended RowFixture to bind on my method but I'm not sure how to extend TypeAdaptor so that it invokes with the attr name..
Any suggestions ?
public class InstanceWrapper {
private Instance instance;
private Map<String, Object> attrs;
public int index;
public InstanceWrapper() {
super();
}
public InstanceWrapper(Instance instance) {
this.instance = instance;
init(); // initialise map
}
private void init() {
attrs = new HashMap<String, Object>();
String attrName;
for (AttrDef attrDef : instance.getModelDef().getAttrDefs()) {
attrName = attrDef.getAttrName();
attrs.put(attrName, instance.getChildScalar(attrName));
}
}
public String getAttribute(String attr) {
return attrs.get(attr).toString();
}
public String description(String attribute) {
return instance.getChildScalar(attribute).toString();
}
}
public class MyDisplayRules extends fit.RowFixture {
#Override
public Object[] query() {
List<Instance> list = PHEFixture.hierarchyList;
return convertInstances(list);
}
private Object[] convertInstances(List<Instance> instances) {
Object[] objects = new Object[instances.size()];
InstanceWrapper wrapper;
int index = 0;
for (Instance instance : instances) {
wrapper = new InstanceWrapper(instance);
wrapper.index = index;
objects[index++] = wrapper;
}
return objects;
}
#Override
public Class getTargetClass() {
return InstanceWrapper.class;
}
#Override
public Object parse(String s, Class type) throws Exception {
return super.parse(s, type);
}
#Override
protected void bind(Parse heads) {
columnBindings = new TypeAdapter[heads.size()];
for (int i = 0; heads != null; i++, heads = heads.more) {
String name = heads.text();
String suffix = "()";
try {
if (name.equals("")) {
columnBindings[i] = null;
} else if (name.endsWith(suffix)) {
columnBindings[i] = bindMethod("description", name.substring(0, name.length()
- suffix.length()));
} else {
columnBindings[i] = bindField(name);
}
} catch (Exception e) {
exception(heads, e);
}
}
}
protected TypeAdapter bindMethod(String name, String attribute) throws Exception {
Class partypes[] = new Class[1];
partypes[0] = String.class;
return PHETypeAdaptor.on(this, getTargetClass().getMethod("getAttribute", partypes), attribute);
}
}
For what it's worth, here's how I eventually worked around the problem:
I created a custom TypeAdapter (extending TypeAdapter) with the additional public attribute (String) attrName. Also:
#Override
public Object invoke() throws IllegalAccessException, InvocationTargetException {
if ("getAttribute".equals(method.getName())) {
Object params[] = { attrName };
return method.invoke(target, params);
} else {
return super.invoke();
}
}
Then I extended fit.RowFixture and made the following overrides:
public getTargetClass() - to return my class reference
protected TypeAdapter bindField(String name) throws Exception - this is a protected method in ColumnFixture which I modified so that it would use my class's getter method:
#Override
protected TypeAdapter bindField(String name) throws Exception {
String fieldName = camel(name);
// for all attributes, use method getAttribute(String)
Class methodParams[] = new Class[1];
methodParams[0] = String.class;
TypeAdapter a = TypeAdapter.on(this, getTargetClass().getMethod("getAttribute", methodParams));
PHETypeAdapter pheAdapter = new PHETypeAdapter(fieldName);
pheAdapter.target = a.target;
pheAdapter.fixture = a.fixture;
pheAdapter.field = a.field;
pheAdapter.method = a.method;
pheAdapter.type = a.type;
return pheAdapter;
}
I know this is not a neat solution, but it was the best I could come up with. Maybe I'll get some better solutions here :-)