I wrote this method:
public <T> T jsonToObject(String json, Class<T> klazz) {
ObjectMapper mapper = new ObjectMapper();
T object = null;
try {
object = mapper.readValue(json, klazz);
} catch (IOException e) {
e.printStackTrace();
}
return object;
}
I want to call it:
List<Device> devicesList= jsonUtils.jsonToObject(response.getEntityInputStream(), new TypeLiteral<List<Device>.class);
what is the correct way to pass a Class<T> of List<Device> ?
Well, if you want to call it with a TypeLiteral (and that's indeed the way to capture the generic type of the list), your method needs to accept a TypeLiteral<T> as argument, not a Class<T>.
And the caller needs to use an anonymous class to properly capture the type:
jsonUtils.jsonToObject(response.getEntityInputStream(),
new TypeLiteral<List<Device>>() {});
Related
I have certain entity classes which I generated from json file. I want to check my entity classes are correct so I have a parseDescriptor method and am reading the value ttoa generic class type. Can some one please tell how can I display the content from generic class object(ret in my case)?
public <T> T parseDescriptor(String json, Class<T> c) throws IOException {
try {
ObjectMapper mapper = new ObjectMapper();
mapper.getDeserializationConfig().disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.getDeserializationConfig().enable(DeserializationConfig.Feature.USE_ANNOTATIONS);
T ret = mapper.readValue(json, c);
if (!(ret instanceof ReportDescriptor)) {
throw new IllegalArgumentException("The specified class of type "+c.getCanonicalName()+" does not extend ReportDescriptor");
}
return ret;
}
catch (JsonParseException jpe) {
:
:
If ret is an instance of ReportDescriptor, you can cast your object to that class. You can then use the functions of that class to do with it what you want.
//Cast the object
ReportDescriptor reportDescriptor = (ReportDescriptor) ret;
//For example, call the toString() function of that class
reportDescriptor.toString();
Consider this External execution class
public class ExternalCommands {
private Logger log = LoggerFactory.getLogger(ExternalCommands.class);
private static final ObjectMapper objectMapper = new ObjectMapper();
public <T> CustomResponse<T> executeQuery(Clients client, Query query, Class<T> classType) throws Exception {
if (Objects.isNull(clients))
throw new Exception("external client is null in external commands");
log.debug("Query : {}", query);
Response queryResponse = clients.getElasticClient().executeQuery(query);
log.debug("query response : {}", queryResponse);
if (queryResponse.status() == 200) {
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(CustomResponse.class, classType); // This is the coding sin m talking about
return objectMapper.readValue(queryResponse.body().asInputStream(), javaType);
}
}
}
So Basically this executeQuery function fetches all the data as per query from an elastic client and deserialise it as per the generic classType as sent in function BUT
this is more like explicitly doing the deserialisation instead of using Generics.
See below code on how this execution works along with inline comments :
public ArrayList<EmpData> getEmpData() throws Exception {
ArrayList<EmpData> merchantUrnMap = new ArrayList<>();
List<Filter> filters = new ArrayList<>();
filters.add("Added filter 1 here");
filters.add("Added filter 2 here");
filters.add("Added filter 3 here");
ExternalCommands commands = new ExternalCommands();
Query query = commands.getQuery(filters);
// get "clients"
// this is how it works now
CustomResponse<EmpData> response = commands.executeQuery(clients, query, EmpData.class);
// this is how i WANT IT TO WORK - without passing "EmpData.class"
// but in this case <T> in "CustomResponse<T>" would not deserialise to "EmpData"
// resulting in LinkedHashMap in deseralised object instead of class object
// CustomResponse<EmpData> response = commands.<EmpData>executeQuery(clients, query);
// some operations
return response
}
any suggestions on how to achieve this?
Case 1: assuming that constructParametricType requires the classType argument to function properly, and you can't change the implementation of methods/classes that are implied but not provided in your posted code.
Your proposed method signature/invocation not possible due to type erasure in Java.
You use classType in the constructParametricType(CustomResponse.class, classType) call, and you're trying to replace classType with T somehow. This is impossible, because when the code is compiled, the T is erased completely. There is no way to do something like constructParametricType(CustomResponse.class, T.class) because T doesn't exist at runtime.
The correct solution is to pass in the class as a method argument, which is precisely what your existing approach does.
Case 2: you really want to have the call commands.<EmpData>executeQuery(clients, query); and you're willing to change anything to achieve that goal.
Since we cannot pass T as an argument to constructParametricType, it must be called as constructParametricType(CustomResponse.class), yet it needs to return a JavaType representing CustomResponse<T>. The only way to do that is to declare
<T> JavaType<T> constructParametricType(Class<?> cls)
Note that JavaType now also has to be parameterized for the same reason (we can't get T at runtime). Finally, we have to declare
CustomResponse<T> readValue(InputStream stream, JavaType<T> javaType)
to match the declared return type of executeQuery.
After all of these changes, the line
CustomResponse<EmpData> response = commands.<EmpData>executeQuery(clients, query);
should compile. Here's a minimal example:
class CustomResponse<T> {}
class Clients{}
class Query{}
class EmpData{}
class ObjectMapper {
JavaTypeFactory getTypeFactory() {
return new JavaTypeFactory();
}
<T> CustomResponse<T> readValue(InputStream s, JavaType<T> j) {
return new CustomResponse<>();
}
}
class JavaTypeFactory {
<T> JavaType<T> constructParametricType(Class<?> cls) {
return new JavaType<>(cls);
}
}
class JavaType<T> {
JavaType(Class<?> cls) {}
}
class ExternalCommands {
private static final ObjectMapper objectMapper = new ObjectMapper();
public <T> CustomResponse<T> executeQuery(Clients clients, Query query) throws Exception {
InputStream queryResponseStream = null;
JavaType<T> javaType = objectMapper.getTypeFactory().<T>constructParametricType(CustomResponse.class);
return objectMapper.readValue(queryResponseStream, javaType);
}
}
class SomeClass {
public void getEmpData() throws Exception {
ExternalCommands commands = new ExternalCommands();
Query query = null;
Clients clients = null;
CustomResponse<EmpData> response = commands.<EmpData>executeQuery(clients, query);
}
}
Beware that some of the described changes might not be easy/possible given the rest of your system (especially parameterizing JavaType), and I don't recommend this approach. I recommend sticking with what you have; it's the cleanest approach IMO.
I am currently working on a serialization routine which uses a library of generically typed adapters. If the object being serialized is an instance of one of the specific adapters I have, then I need to call that adapter on the object prior to performing my other serialization procedures.
The following code works:
private final static String serialize(Object obj, Map<Class<?>,
XmlAdapter<?,?>> classToAdapterMap) throws JAXBException
{
Object adaptedObj = null;
for (Class<?> clazz : classToAdapterMap.keySet()) {
if (clazz.isInstance(obj)) {
XmlAdapter<?,?> adapter = classToAdapterMap.get(clazz);
Class<?>[] argTypes = new Class[] {clazz};
try {
Method method = adapter.getClass().getMethod("marshal", argTypes);
adaptedObj = method.invoke(adapter, obj);
break;
} catch (Exception e) {
// handle method retrieval and invocation related exceptions
}
}
}
// serialize
}
However, I had originally thought that I would be able to do this more simply, for example with code like:
/* DOES NOT WORK */
private final static String serialize(Object obj, Map<Class<?>,
XmlAdapter<?,?>> classToAdapterMap) throws JAXBException
{
Object adaptedObj = null;
for (Class<?> clazz : classToAdapterMap.keySet()) {
if (clazz.isInstance(obj)) {
XmlAdapter<?,?> adapter = classToAdapterMap.get(clazz);
adaptedObj = adapter.marshal(clazz.cast(obj));
break;
}
}
// serialize
}
Clearly the problem is that the wildcard generically typed adapter isn't guaranteed to handle an object of type clazz. However, I can't indicate that these two are the same by changing the method signature—as I might otherwise do—to private final static <T> String serialize(Object obj, Map<Class<T>, XmlAdapter<?,T>> classToAdapterMap), because the map needs to hold adapters of all different types.
What would be a better way to do this? Or should I stick with the Reflection based solution?
Thanks in advance,
-Dan
There are several solutions to circumvent this problem.
Most likely, the easiest one is using raw types: don't specify the type parameters for the adapter, and the compiler will happily accept the marshall call (with a raw type warning of course):
XmlAdapter adapter = classToAdapterMap.get(clazz);
adaptedObj = adapter.marshal(obj);
(This is actually roughly the same solution as Bastian's, without the intermediate type)
If you don't like raw types, you may choose the unchecked cast to an Object-parameterized adapter. It's not really better, but it also works (by tricking the compiler…):
XmlAdapter<?, Object> adapter = (XmlAdapter<?, Object>) classToAdapterMap.get(clazz);
adaptedObj = adapter.marshal(obj);
My last solution is to use a type parameter at the method level. This time, what you do is semantically correct (as long as the map itself is correct), and the unchecked cast really means “I know what I am doing here”:
private final static <T> String serialize(T obj, Map<Class<?>,
XmlAdapter<?,?>> classToAdapterMap) throws JAXBException
{
Object adaptedObj = null;
for (Class<?> clazz : classToAdapterMap.keySet()) {
if (clazz.isInstance(obj)) {
try {
XmlAdapter<?, ? super T> adapter = (XmlAdapter<?, ? super T>) classToAdapterMap.get(clazz);
adaptedObj = adapter.marshal(obj);
break;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
// serialize
}
The semantic correctness comes from the following:
you may consider T to be the actual class of obj since T is a method-bound parameter, not used elsewhere in the signature;
clazz is a super type of the type of T since we checked clazz.isInstance(obj);
adapter can handle instances of clazz or a super-type of it since it is how the map was built;
by consequent, adapter can handle all instances of an (unknown) super type of T, hence the ? super T declaration.
There is a simpler and safer way without using reflection:
At first, we need a small specialization of the XmlAdapter as it allows us to ask the adapter for the type it can handle.
public abstract class TalkingXmlAdapter<ValueType, BoundType> extends XmlAdapter<ValueType, BoundType> {
public abstract Class<BoundType> getBoundType();
}
My custom adapters now need to extend TalkingXmlAdapter:
public class AppleXmlAdapter extends TalkingXmlAdapter<String, Apple> {
#Override
public Class<Apple> getBoundType() {
return Apple.class;
}
#Override
public Apple unmarshal(String v) throws Exception {
System.out.println("Unmarshalling Apple");
return new Apple();
}
#Override
public String marshal(Apple v) throws Exception {
System.out.println("Marshalling Apple");
return "Apple";
}
}
public class BananaXmlAdapter extends TalkingXmlAdapter<String, Banana> {
#Override
public Class<Banana> getBoundType() {
return Banana.class;
}
#Override
public Banana unmarshal(String v) throws Exception {
System.out.println("Unmarshalling Banana");
return new Banana();
}
#Override
public String marshal(Banana v) throws Exception {
System.out.println("Marshalling Banana");
return "Banana";
}
}
That allows us to write a simplified serialization method:
public class SimpleSerializer {
public static final String serialize(Object obj, List<TalkingXmlAdapter> allAdapters) throws Exception {
Object adaptedObj = null;
for (TalkingXmlAdapter adapter : allAdapters) {
if (adapter.getBoundType().isInstance(obj)) {
adaptedObj = adapter.marshal(obj);
break;
}
}
// serialize
System.out.println("Simple serializing for " + obj.toString());
return "Simply serialized " + obj.toString();
}
}
Using the code e.g. like in the subsequent listing shows the behavior you want:
List<TalkingXmlAdapter> allAdapters = new ArrayList<>();
allAdapters.add(new AppleXmlAdapter());
allAdapters.add(new BananaXmlAdapter());
SimpleSerializer.serialize(new Banana(), allAdapters);
SimpleSerializer.serialize("Lemmon", allAdapters);
SimpleSerializer.serialize(new Apple(), allAdapters);
Output:
Marshalling Banana
Simple serializing for generic.adapter.Banana#659e0bfd
Simple serializing for Lemmon
Marshalling Apple
Simple serializing for generic.adapter.Apple#2a139a55
To sum this up, the solution gives you following advantages:
You don't need reflection which simplifies your code.
You need fewer generic programming in your serialization routine which simplifies your code.
The solution is more safe. Note that no type cast is needed. Every adapter accepts the type Object. However by using the generic method getBoundType() you can ensure the specific runtime type is the correct one. When building your map as in the reflection solution, a wrongly mapped class results in a runtime exception. In the proposed solution the super class TalkingXmlAdapter enforces each adapter to state their correct type by using generics.
The price you pay is:
Introduction of a new super type.
Requires small adaptions for your custom adapters.
Hope that helps!
Certainly I'm quite new in all this Java stuff, so I have a question, I'm trying to deserialize a response obtained on a WCF service, everything works fine, but, I'm trying to make a generic function to do this.
Basically what I do is
public List<msg> GetService(String method){
List<msg> response = new ArrayList<msg>();
Type msgType = new TypeToken<List<msg>>(){}.getType();
//Obtaining result
response = uJSON.fromJson(serviceResponse, msgType);
//uJSON is an instance of Gson library, for deserializing it just needs
//the service response and a Class<T> or Type to reflect the obtained message
}
What I'm trying to do is obtaining the Type "msg" generic, it means...
public <thing> void GetInstanceService(String method){
List<thing> response = new ArrayList<thing>();
Type rType2 = new TypeToken<List<thing>>(){}.getType(); //Got java.util.List<thing>
//And when I'm trying to deserialize I just obtain a List of object
//[java.lang.Object#5c7a987e, java.lang.Object#74b1a7a0]
type2 = uJSON.fromJson(new String(entity), rType2);
}
But I'm calling like this.
comm.<msgType>GetInstanceService("listTestType");
So, when I call "GetInstanceService", "thing" is "msgType" Type, for the
List<thing> and also response shouldn't be List<msgType> instead of List <Object>?
Besides, when I'm trying to explicitly pass the type through a "Type" parameter, it just causes me compilation time error like this.
public void GetInstanceService(Type type){
List<type> type2 = new ArrayList<type>(); //Compilation time error
//Or
msgType oType = new msgType();
Class classType = oType.getClass();
List<classType> type3; //Compilation time error
}
So, if none of these attempts was effective, how could I set the type for deserialization?
Guava class TypeToken does not support that mode of usage. You are creating the type token with a type variable and there not enough information for it to reconstruct List<String> from List<T>. You should create an instance of TypeToken where you have all the required compile-time information.
The documentation says:
Note that it's critical that the actual type argument is carried by a
subclass. The following code is wrong because it only captures the <T>
type variable of the listType() method signature; while <String> is
lost in erasure:
class Util {
static <T> TypeToken<List<T>> listType() {
return new TypeToken<List<T>>() {};
}
}
TypeToken<List<String>> stringListType = Util.<String>listType();
But as said above, you can instantiate the TypeToken at call-site, where all type info are available, and then pass it as a parameter. Something like this:
public <thing> void GetInstanceService(String method, TypeToken<List<thing>> token){
List<thing> response = new ArrayList<thing>();
Type rType2 = token.getType();
type2 = uJSON.fromJson(new String(entity), rType2);
}
comm.GetInstanceService("listTestType", new TypeToken<List<msgType>>() {});
Update
Paul Bellora notes that you can also accept a parameter TypeToken<thing> token, and construct a TypeToken<List<thing>> inside the method from that token:
public <thing> void GetInstanceService(String method, TypeToken<thing> token) {
List<thing> response = new ArrayList<thing>();
Type rType2 = new TypeToken<List<thing>>() {}
.where(new TypeParameter<thing>() {}, token); // where() binds "thing" to token
.getType();
type2 = uJSON.fromJson(new String(entity), rType2);
}
comm.GetInstanceService("listTestType", new TypeToken<msgType>() {});
Due to something called type erasure, the class object you need is not available at runtime.
However, there is a standard work-around: pass a type token into your method, like this:
public <T> List<T> getService(String method, Class<T> c) {
// the caller has passed in the class object
List<T> list = new ArrayList<T>();
// fill list
return list;
}
I have a method that return a list of objects and I want to call it by the invoke method of the class Method. The only problem is that invoke method returns an Object and not a list<Object>.
The code is here:
Class<? extends AnObject> anObject = MyObject.getClass();
Method myMethod = MyObject.getMethod("getListObject");
Object objject = method.invoke(MyObject); // I want it to return list
How can I solve it?
Class<? extends AnObject> anObject = MyObject.getClass();
Method myMethod = MyObject.getMethod("getListObject");
List object = (List)myMethod.invoke(anObject);
Just typecast it:
List list = (List)method.invoke(...);
You can explicitly cast the result to the type you desire.
List<?> theList = (List<?>) method.invoke(anObject, new Object[] {})
This may result in a ClassCastException in runtime if the method doesn't return the type you expected.
How about letting dp4j's annotations processor figure it out?
#com.dp4j.Reflect //or #org.junit.Test
public void test(){
Object objject = MyObject.getListObject();
}
You can also print the injected code with -Averbose=true.
what let me to this page is
when I tried to invoke a protected method without making it Accessible
take look at my now working version
DoPubService service = SpringAdapter.getBean("frontendDoPubService", DoPubService.class);
try {
Method runUrlReplacerMethod = service.getClass().getDeclaredMethod("runUrlReplacer", String.class, String.class, String.class);
return runUrlReplacerMethod.invoke(service, "10.21019/qna-900031", "abs", explanation);
} catch (Exception e) {
e.printStackTrace();
}
this is not working because the Method should be Accessible before invoking it
runUrlReplacerMethod.setAccessible(true);
Java 8 solution:
List<?> list = (List) invokeResult;
List<String> = list.stream().map(el -> (String) el).collect(Collectors.toList());
Change String to type you need.