How to make a generic function for Jackson/ObjectMapper in java? - java

So I have these methods:
private List<ClientRepresentation> toClientRepresentation(String json) {
try {
return objectMapper.readValue(json, new TypeReference<List<ClientRepresentation>>() {
});
} catch (JsonProcessingException e) {
throw new RuntimeException("Could not parse json.");
}
}
private List<RoleRepresentation> toRoleRepresentation(String json) {
try {
return objectMapper.readValue(json, new TypeReference<List<RoleRepresentation>>() {
});
} catch (JsonProcessingException e) {
throw new RuntimeException("Could not parse json.");
}
}
private List<UserRepresentation> toUserRepresentation(String json) {
try {
return objectMapper.readValue(userJson, new TypeReference<List<UserRepresentation>>() {
});
} catch (JsonProcessingException e) {
throw new RuntimeException("Could not parse json.");
}
}
You can see these are similar... Would like to have one method for these... Something like this:
private List<T> toObjectsList(String json, Class classToConvert) {
try {
return objectMapper.readValue(json, new TypeReference<List<classToConvert>>() {
});
} catch (JsonProcessingException e) {
throw new RuntimeException("Could not parse json.");
}
}
Is it possible? How?
Thanks in advance.

Note first that Class<?> can be used to convey only type-parameter-less constructs. Thus, something like List<String> cannot be conveyed with it (only 'List' - any and all lists, unparameterized - that is the only thing Class can convey). If you ever want toObjectsList to return something like a List<Map<String, Object>> you are dead in the water on this: The entire setup fundamentally can't do it. So, first consider whether this is a good idea: You are handicapping yourself, that your toObjectsList method cannot ever return lists of type-argsed types. Presumably, if you're willing to document this and sign up to tacle such needs by not using toObjectsList and writing a custom readValue-based method instead, you're good to go.
Assuming you're on board with this restriction, the question boils down to: How does one construct an STT (Super-Type-Token, that TypeReference thing with the brackets after it) dynamically?
This should be doable, using the TypeFactory class.
For example, constructParametricType(List.class, List.class, classToConvert);.
We then need to address some other errors in your code:
That exception handling is deplorable. You're tossing away all the useful info, and replacing it with entirely useless information. Don't write a catch block that fails to address the issue, unless that catch block preserves all information. Also, by convention exception messages do not end in punctuation.
You are using a <T> in these methods, but you need to declare that T exists, first.
Class, itself, is parameterized.
This gets us to:
private <T> List<T> toObjectsList(String json, Class<T> classToConvert) {
JavaType typeTarget = TypeFactory.defaultInstance()
.constructParametricType(List.class, List.class, classToConvert);
try {
return objectMapper.readValue(json, typeTarget);
} catch (JsonProcessingException e) {
// pass the original exception along as cause, preserving info.
throw new RuntimeException("Could not parse json", e);
}
}
(You may have to cast to List<T>, but I don't think it'll be needed due to some dubious hackery on jackson's part).

Related

Throwing exception passed by parameter to a function - java

im currently working in a complete generic scenario in which i map a json as string to a dto class. That works fine with my function mapJsonToDto but im trying to make it more generic so that the developer who uses this function can also specify what exception to be thrown. So they can catch as they like. With this i avoid catching an IOException. Letting the function handle everything.
public class MapperUtils {
public <T extends Throwable> Object mapJsonToDto(Class<?> dtoClass, String jsonDto, T exceptionToThrow) throws IOException {
Object dto = null;
try {
dto = new ObjectMapper().readValue(jsonDto, dtoClass);
} catch (IOException e) {
throw new exceptionToThrow();
}
return dto;
}
}
I cannot understand how to pass an exception class instance to a function and throwing that specific as well.
Instead of passing the exception to throw (which would then have a completely wrong stack trace), I think you'd want a function that converts an exception from one type to another:
public <T extends Throwable, D> D mapJsonToDto(Class<D> dtoClass, String json, Function<IOException, T> exceptionMapper) throws T {
try {
return new ObjectMapper().readValue(json, dtoClass);
// if readValue doesn't do the casting right, try:
return dtoClass.cast(new ObjectMapper().readValue(json, dtoClass);
} catch (IOException e) {
throw exceptionMapper.apply(e);
}
}
And an example:
Person p = mapJsonToDto(Person.class, "{name: \"Joe\"}",
e -> new IllegalArgumentException("malformed JSON", e));
As a general rule, though, this seems like boneheaded design. If you find the IOException overly general, then you can't handwave the problem away by allowing the caller to provide a no doubt similarly overly general mapper. The only way out for a caller is to do a deep dive on the exception and write, I dunno, an if/elseif block with a ton of levels to it to try to ascertain the real problem e.g. via analysing the message, which is all sorts of ugly.
Either you don't care about that level of detail and you should therefore just stick with IOException (what point is there adding code and pointless layers of indirection?), or you do care and this isn't good enough; you'd want to design a better error system. Except, that's not your job, that'd be ObjectMapper.readValue's job. Which is why the IOException it throws should probably just be sent on unmolested.
Your example is nearly done.
I changed only the throws Type to T and throw the given exception.
public <T extends Throwable> Object mapJsonToDto(Class<?> dtoClass, String jsonDto, T exceptionToThrow) throws T {
Object dto = null;
try {
dto = new ObjectMapper().readValue(jsonDto, dtoClass);
} catch (IOException e) {
throw exceptionToThrow;
}
return dto;
}
Call: mapJsonToDto(String.class, "helo", new IllegalStateException());

Java - Avoid pattern duplication

Java
In my server code, for each method I will have to do the following
#Path("...")
public Response function(...) {
try {
result = do some work to get result
log the result
return result
} catch (Exception e) {
log error
return error
}
}
I will have to duplicate this pattern everywhere and only changing the do some work to get result part for new endpoint.
Is there a way that I can do to reduce this is duplication?
P.S. do some work to get result can be very something like getting a Json file, or accessing multiple database and calculate the result, and need to be in try-catch block.
You can create a function that looks like this:
public static <T> T doTheThing(Supplier<T> supp, Supplier<T> errorSupp) {
try {
T result = supp.get();
//log the result
return result;
} catch (Exception e) {
//log error
return errorSupp.get();
}
}
Then you can pass suppliers for a) the actual work and b) the potential error to this function.

Dynamically loading method from external class

I am trying to load methods Customer.cypher and Customer.cypherCBC method from my class Configuration. Customer class is rendering from different environments so few environmets are having cypherCBC() and cypher() method and few are having only cypher() method.
Now i want to check if cypherCBC if not there in Customer class then load cypher() method. My function is so far;
try {
Class<?> customerClass = Class.forName("com.myapp.impl.service.Customer");
Object obj = customerClass.newInstance();
//here getting "NoSuchMethodException" exception
Method methodCBC = customerClass.getDeclaredMethod("cypherCBC", String.class); //line - 7
if(methodCBC.getName().equals("cypherCBC")){
methodCBC.invoke(obj, new String(dbshPass));
System.out.println("CYPHER_CBC: "
+ methodCBC.invoke(obj, new String(dbshPass)));
}else{
Method method = customerClass.getDeclaredMethod("cypher", String.class);
method.invoke(obj, new String(dbshPass));
System.out.println("CYPHER: " + method.invoke(obj, new String(dbshPass)));
}
}catch (Exception e){
e.printStackTrace();
}
Getting an error at line 7.
NoSuchMethodException:
com.myapp.impl.service.Customer.cypherCBC(java.lang.String)
that means for particular environment class Customer doesn't having cypherCBC() method, but ideally it should come in else part and execute cypher() method.
Class<?> client = null;
Object obj = null;
try{
client = Class.forName("com.myapp.impl.service.Client");
obj = client.newInstance();
}catch (InstantiationException ex) {
System.err.println("Not able to create Instance of Class");
} catch (IllegalAccessException ex) {
System.err.println("Not able to access Class");
} catch (ClassNotFoundException ex) {
System.err.println("Not able to find Class");
}
try {
Method methodCBC = client.getDeclaredMethod("cypherCBC", String.class);
System.out.println("CYPHER_CBC: " + methodCBC.invoke(obj, new String(dbshPass)));
}catch (NoSuchMethodException ex) {
System.err.println("Not able to find Method on class");
ex.printStackTrace();
} catch (Exception e){
e.printStackTrace();
}
That is exactly what is to be expected: getDeclaredMethod() throws that exception when no method exists that meets your specification. And you are wondering that it throws an exception if the required method is missing? Hint: better read the javadoc next time. Don't assume that something does something, but verify your assumptions!
Besides: read your code again. What is it doing? You are asking "give me the method named 'foo'". And then, your next step is to ask that method "is your name 'foo'". So even without reading javadoc, it should become clear that your logic is flawed.
As solution, you can implement a non-throwing lookup yourself, like
private Method lookupCypher(Class<?> client, String methodName) {
for (Method declaredMethod : client.getDeclardMethods()) {
if (declaredMethod.getName().equals(methodName)) {
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
if (parameterTypes.length == 1 && parameterTypes[0].equals(String.class)) {
// so declaredMethod has the given name, and takes one string as argument!
return declaredMethod;
}
}
// our search didn't reveal any matching method!
return null;
}
Using that helper method, you can rewrite your code to:
Method toInvoke = lookupCypher(client, "cypherCBC");
if (toInvoke == null) {
toInvoke = lookupCypher(client, "cypher");
}
toInvoke(obj, new String ...
Or, with the idea from hunter in mind; a much more "OO like" version:
interface CustomerCypherWrapper {
void cypher(String phrase);
}
class NewCustomerWrapper() implements CustomerCypherWrapper {
#Override
void cypher(String phrase) {
new Customer.cypherCBC(phrase);
}
}
class oldCustomerWrapper() implements CustomerCypherWrapper {
#Override
void cypher(String phrase) {
new Customer.cypher(phrase);
}
}
And your client code boils down to:
CustomerCypherWrapper wrapper =
(lookupCypher(..., "cypherCBC") == null)
? new NewCustomerWrapper()
: new OldCustomerWrapper();
wrapper.cypher();
[ I hope you notice that my version A) is easier to read and B) doesn't contain any duplicated code any more. ]
And yes, an alternative implementation of the lookup method could just go like
private Method lookupCyper(Client<?>, String methodName) {
try {
return client.getDeclaredMethod(methodName, String.class);
} catch ....
and return null;
}
... return your public cypherCBC method
But that is an "uncommon practice" in Java. In Java, we ask for permission; instead of forgiveness. Unlike other languages
if you compile the application with a Customer class which has both method,you can use reflection once to check whether the cypherCBC method available or not at runtime, then you can keep that status, you can call the method without using reflection
if(newVersion)
{
customer.cypherCBC(arg);
}
else
{
customer.cypher(arg);
}
But to write a better application,you should use two version baselines.
even though this is a small code fragment you should setup a another module to hide this Customer class and its interactions,that module should have two versions. but your main module has only single version.Now when you you deliver the application , product should be packaged with right version baseline based on compatibility for the target environment.
Although reflection works (as explained in the other answers). if you have control over the Customer class, you can try a non-reflection approach.
interface CBCCypherable {
public String cypherCBC(String pass);
}
You can now have two versions of Customer class, one that implements CBCCypherable and one that doesn't. And when you call it, it looks like this:
Customer c = new Customer();
if (c instanceof CBCCypherable) {
((CBCCypherable)c).cypherCBC(pass);
} else {
c.cypher(pass);
}
What you get with this solution is much simpler code, and as a bonus the compiler will check that you use the correct method name and parameter types. Unlike with reflection, where that's all your job, and you have to run the code to find out if something's wrong.
P.s.: I don't know if this is just sample code or you are really encrypting/hashing passwords here, but it's generally considered a bad idea to roll your own security code.

How to loop over a Class attributes in Java - from a given list (NOT reflection getDeclaredFields())?

How can I iterate over the attributes of an object, with the attribute names provided in a list/array - NOT all attributes, like using reflection & getDeclaredFields().
public class MyClass
{
public type1 att1;
public type2 att2;
public type3 att3;
public MyClass(
att1="helo";
att2="bye";
att3="morning";
);
...
public void function()
{
String myStrings[];
myStrings = new String[] { "att2", "att3" };
MyClass myobject = new MyClass();
for(var in myStrings)
{
System.out.println(var);
System.out.println(myobject.var);
System.out.println();
}
}
}
Your question is somewhat ambiguous about using reflection. If you are OK with reflection, but want specific fields only without iterating over getDeclaredFields(), then the following code should work for you:
for (String var : myStrings) {
Field field = MyClass.class.getDeclaredField(var);
field.setAccessible(true);
System.out.println(var);
System.out.println(field.get(myObject));
System.out.println();
}
Note that this code works for private fields, too. Also, keep in mind that you'll have to handle exception associated with the reflection calls.
UPDATE: Exceptions thrown in this code.
MyClass.class.getDeclaredField(var) declares a checked NoSuchFieldException. You must handle it because obviously there is no mechanism to make sure that the fields in myString match an actual implementation of MyClass.
field.get(myObject) throws a checked IllegalAccessException if the field is inaccessible. Which it should not be because of field.setAccessible(true), but you still have to catch or re-throw the exception.
There are also unchecked exceptions you may want to handle. See the javadoc for details
java.lang.Class.getDeclaredField(String)
java.lang.reflect.AccessibleObject.setAccessible(boolean) inherited by java.lang.reflect.Field
java.lang.reflect.Field.get(Object)
You probably want to use some technology that builds on top of JavaBeans / BeanInfo. Apache Commons / BeanUtils is a good starting point here.
Please refer to this previous answer of mine for more info:
https://stackoverflow.com/a/5856982/342852
But if you just want to use fields, not bean properties, here's a Java 8 method to do so:
public static Map<String, Object> getFieldProperties(Object o, Collection<String> fields) {
Class<?> type = o.getClass();
return fields.stream().map(n -> {
try {
return type.getDeclaredField(n);
} catch (NoSuchFieldException e) {
throw new IllegalStateException(e);
}
}).collect(Collectors
.toMap(
(Function<Field, String>) Field::getName,
(Function<Field, Object>) field -> {
try {
field.setAccessible(true);
return field.get(o);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}));
}
Unfortunately the checked exceptions make this more verbose than it would need to be.

String.format using a exception.getMessage() as a format

I have a question related to String.format in JAVA.
My HibernateDao Class is responsible for persisting entities and will throw an exception in case I have any constrain violation. The message contains a %s and will be used as a format in the upper layers, as I should be worried about types in this layer, thus can't identify what object I could not persist.
public Entity persistEntity(Entity entity) {
if (entity == null || StringUtils.isBlank(entity.getId()))
throw new InternalError(CANNOT_INSERT_NULL_ENTITY);
try {
getHibernateTemplate().save(entity);
} catch (DataAccessException e) {
if (e.getCause() instanceof ConstraintViolationException)
throw new HibernateDaoException("%s could not be persisted. Constraint violation.");
throw new HibernateDaoException(e);
}
return entity;
}
And then in my DaoHelper Class I will catch this exception and throw a new one, with a formatted message.
//Correct Code
public Entity create(Entity object) throws MyException {
try {
return this.hibernateDao.persistEntity(object);
} catch (HibernateDaoException he) {
String format = he.getMessage();
throw new MyException(String.format(format,object.getClass().getSimpleName()));
}
}
My question is, why I cannot directly call the he.getMessage() in my String.format method?? And must use a 'tmp' variable instead... It just won't substitute the %s from the string.
//What I wished to do, but I cant.
public Entity create(Entity object) throws MyException {
try {
return this.hibernateDao.persistEntity(object);
} catch (HibernateDaoException he) {
throw new MyException(String.format(he.getMessage(),object.getClass().getSimpleName()));
}
}
Thx in advance.
This should be closed, as the intended behavior is working. As #Kal and #highlycaffeinated commented, calling the getMessage() directly does work, something must have happened with my build and did not update correctly. However the messages do appear correctly now.
Thanks for the quick answers :)

Categories