I have a requirement to get JSON
input Pojo instance and I am using Jackson 2
library and below readValue
method could deserialise using typeReferencing :
POJO_ClassName p = mapper.readValue(new TypeReference< POJO_ClassName >() {});
But the problem is that as POJO
is created and loaded at runtime dynamically, how do I get JSON
to POJO
instance/object as I do not have fully qualified class (POJO_ClassName) name for above statement?
Note: I use jsonSchema2pojo
library to generate POJO
classes at runtime.
Here is code snippet, I am using to generate POJO
for JSON
at runtime
and trying
String classPath="com.EnrichmentService.Thread72";
String classLocation = System.getProperty("user.dir")
+ "/src/main/java"; JCodeModel codeModel = new JCodeModel();
final RuleFactory ruleFactory = new RuleFactory(config,
new Jackson2Annotator(config), new SchemaStore());
final SchemaMapper mapperSchema = new SchemaMapper(ruleFactory,
new SchemaGenerator());
mapperSchema.generate(codeModel, "EsRootDoc",classPath, json);
codeModel.build(new File(classLocation)); // generates pojo classes
// Till above jsonSchema2Pojo pojo generation all Good !!
// EsRootDoc instance is needed for further drools drl validations.
com.EnrichmentService.Thread72.EsRootDoc p = mapper.readValue(new TypeReference<com.EnrichmentService.Thread72.EsRootDoc>() {});
// see alternative way as well in my 24Aug17 edit at the end of this question
But as com.EnrichmentService.Thread72.EsRootDoc
has yet not been generated compiler would error to class not Found.
Main Points:
1) Same Pojo classes generated at run time iteratively but with different properties as JSON input changes each time.
2) Even tried Object pojo =mapper.readValue(json,Class.forName("com.EnrichmentService.Thread72.EsRootDoc")); as class.forName does not replace an existing class!
Edit 24 Aug17 - Here is my custom class loader :
Note: Indexer is class which load dynamic EsRootDoc/POJO class at run time.
static class TestClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.equals("com.EnrichmentService.Thread72.EsRootDoc")) {
try {
InputStream is = Indexer.class.getClassLoader().getResourceAsStream("com/EnrichmentService/Thread72/EsRootDoc.class");
byte[] buf = new byte[is.available()];
int len = is.read(buf);
Class<?> c=defineClass(name, buf, 0, len);
resolveClass(c);
return c;
} catch (IOException e) {
throw new ClassNotFoundException("", e);
}
}
return getParent().loadClass(name);
}
}
I have tried using above TestClassLoader custom class loader as an alternative way is like this :
Class cls = new TestClassLoader().loadClass("com.EnrichmentService.Thread72.EsRootDoc");
Object obj = cls.newInstance();
cls.getMethod("getCrawlerSource").invoke(obj);
p=mapper.readValue(json, cls); // but here i am getting the same deserialization exception as earlier.
Referred an old answer@ How to replace classes in a running application in java ?
Edit2: 24Aug17 Exception being faced stackTrace is here: https://pastebin.com/ckCu2uWx
Imo there are two approaches to solve that:
or
you do something like that:
String className = "com.EnrichmentService.Thread72.EsRootDoc";
Class<?> clazz = Class.forName(className);
Object object = clazz.getConstructor().newInstance();
Object p = mapper.readValue(json, object.getClass());
If that code fails before mapper.readValue() you have another problem (my guess would be with classloading).
Even better would be something with generics:
String className = "com.EnrichmentService.Thread72.EsRootDoc";
Class<?> clazz = Class.forName(className);
// cannot use dynamically created classes in a static way, just to
// show the point
// com.EnrichmentService.Thread72.EsRootDoc p =
// getObjectFromMessageString(json, clazz);
Object p = getObjectFromString(json, clazz);
public static <T> T getObjectFromString(String json, Class<T> clazz) {
return mapper.readValue(json, clazz);
}
Edit:
I wrote some example code which compiles a class on runtime and then tries to convert to an object of said compiled class. The output was as I expected:
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JackonCustomClassTest {
public static String CLASS_NAME = "EsRootDoc";
public static String PACKAGE_NAME = "com.EnrichmentService.Thread72";
public static String CANONICAL_NAME = PACKAGE_NAME + "." + CLASS_NAME;
public static void main(String args[]) throws Exception {
JackonCustomClassTest mtc = new JackonCustomClassTest();
Class<?> c = null;
String source = null;
// compile class for the first time
source = "package "+PACKAGE_NAME+"; public class "+CLASS_NAME+" { public "+CLASS_NAME+"() { }; public String toString() { return \"Name: not existing\" + \" - className: \" + getClass().getCanonicalName(); }; }";
c = mtc.compileClass(CANONICAL_NAME, source);
System.out.println("class test: " + c.newInstance().toString());
// compile class for the second time
source = "package "+PACKAGE_NAME+"; public class "+CLASS_NAME+" { private String name; public "+CLASS_NAME+"() { }; public String getName() { return name; }; public void setName(String name) { this.name = name; }; public String toString() { return \"Name: \" + name + \" - className: \" + getClass().getCanonicalName(); }; }";
c = mtc.compileClass(CANONICAL_NAME, source);
System.out.println("class test: " + c.newInstance().toString());
mtc.runJackson(c);
}
private void runJackson(Class<?> clazz) throws JsonParseException, JsonMappingException, IOException {
ObjectMapper m = new ObjectMapper();
String string = "{ \"name\": \"asdf\" }";
Object o = m.readValue(string, clazz);
System.out.println("result of conversion: " + o); // Should print "Name: asdf"
}
public Class<?> compileClass(String fullyQualifiedClassName, String source) throws Exception {
// Save source in .java file.
File root = new java.io.File( "./target/test-classes/" );
File sourceFile = new File(root, fullyQualifiedClassName.replace(".", "/") + ".java");
sourceFile.getParentFile().mkdirs();
Files.write(sourceFile.toPath(), source.getBytes(StandardCharsets.UTF_8));
// Compile source file.
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, sourceFile.getPath());
// Load and instantiate compiled class.
// URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { root.toURI().toURL() });
// Class<?> cls = Class.forName(fullyQualifiedClassName, true, classLoader);
Class<?> cls = new TestClassLoader().loadClass(fullyQualifiedClassName);
return cls;
}
static class TestClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.startsWith(PACKAGE_NAME)) {
try {
InputStream is = this.getClass().getClassLoader()
.getResourceAsStream(name.replace(".", "/") + ".class");
byte[] buf = new byte[is.available()];
int len = is.read(buf);
Class<?> c = defineClass(name, buf, 0, len);
resolveClass(c);
return c;
} catch (IOException e) {
throw new ClassNotFoundException("", e);
}
}
return getParent().loadClass(name);
}
}
}
Edit 2:
Updated the code to try your TestClassLoader class - still get the correct (updated) version of the class.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With