online convertir java json jackson jsonschema2pojo

java - convertir - Deserialización de Jackson/TypeReference para la clase pojo cargada dinámicamente



json to object online (3)

Pero como com.EnrichmentService.Thread72.EsRootDoc ​​aún no se ha generado, el compilador generaría un error en la clase no encontrada.

Sí, por supuesto. Si no tiene clase en su JVM, no puede cargarlos.

com.fasterxml.jackson.databind.JsonMappingException: no se puede deserializar

Por favor, den toda stacktrace.

1) Las mismas clases de Pojo se generan en el tiempo de ejecución de forma iterativa pero con diferentes propiedades, ya que la entrada JSON cambia cada vez.

¿Por qué no usas Maps? ¿Por qué no usas una gran clase con todos los campos (y algunos de ellos serán nulos)?

Sí, la clase EsRootDoc ​​se genera iterativamente en tiempo de ejecución y cambios de clase, así como por cambios de json de entrada en cada iteración

Si haces esto en varios hilos, simplemente los sincronizaste, por ejemplo:

final String ConcurrentHashMap<String, Class> dynamicClasses = new ConcurrentHashMap(); Object doInThreadOne(String json, String className) { return mapper.readObject(json, dynamicClasses.get(className)) void doInAnotherThread(String className) { dynamicClasses.put(className, reload(className)); }

Si necesita una consistencia más fuerte, puede usar la sincronización por nombre de clase:

static final String className = "com.EnrichmentService.Thread72.EsRootDoc"; final String Map<String, Class> dynamicClasses = new HashMap(); Object doInThreadOne(String json) { synchronized(className) { return mapper.readObject(json, dynamicClasses.get(className)) } void doInAnotherThread(String className) { synchronized(className) { dynamicClasses.put(className, reload(className)); } }

Método Class.forName utiliza el cargador de clases de la persona que llama. Usas diferentes cargadores de clases, podría ser una razón. Hay sobrecargas, donde puede pasar el cargador de clases.

Actualización para stackrace e información adicional.

Debería agregar @JsonIgnoreProperties (ignoreUnknown = true) a Crawler, porque tiene el campo subCategory, que no está presente en Pojo.

Tengo un requisito para obtener la instancia de Pojo de entrada JSON y estoy usando la biblioteca Jackson 2 y el método readValue continuación podría deserializar el uso de typeReferencing:

POJO_ClassName p = mapper.readValue(new TypeReference< POJO_ClassName >() {});

Pero el problema es que a medida que POJO se crea y se carga dinámicamente en el tiempo de ejecución, ¿cómo obtengo la instancia / objeto JSON a POJO ya que no tengo el nombre de clase completamente calificado (POJO_ClassName) para la declaración anterior?

Nota: jsonSchema2pojo biblioteca jsonSchema2pojo para generar clases POJO en tiempo de ejecución.

Aquí está el fragmento de código, estoy usando para generar POJO para JSON en tiempo de ejecución e intentando

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

Pero como com.EnrichmentService.Thread72.EsRootDoc aún no se ha generado, el compilador generaría un error en la clase no encontrada.

Puntos principales:

1) Las mismas clases de Pojo se generan en el tiempo de ejecución de forma iterativa pero con diferentes propiedades, ya que la entrada JSON cambia cada vez.

2) Incluso probé Object pojo = mapper.readValue (json, Class.forName ("com.EnrichmentService.Thread72.EsRootDoc")); como class.forName no reemplaza una clase existente!

Editar 24 Aug17 - Aquí está mi cargador de clases personalizado:

Nota: Indexer es una clase que carga la clase dinámica EsRootDoc ​​/ POJO en tiempo de ejecución.

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); } }

He intentado utilizar el cargador de clases personalizado TestClassLoader anterior, ya que una forma alternativa es la siguiente:

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.

Remitió una respuesta anterior @ ¿Cómo reemplazar clases en una aplicación en ejecución en Java?

Edit2: 24Aug17 Excepción frente a stackTrace está aquí: https://pastebin.com/ckCu2uWx


Imo hay dos enfoques para resolver eso:

  1. crear y compilar las clases a la hora de comilar (por ejemplo, con maven y jaxb)

o

  1. haces algo como eso

    String className = "com.EnrichmentService.Thread72.EsRootDoc"; Class<?> clazz = Class.forName(className); Object object = clazz.getConstructor().newInstance(); Object p = mapper.readValue(json, object.getClass());

Si ese código falla antes de mapper.readValue () tienes otro problema (supongo que sería con la carga de clases).

Aún mejor sería algo con genéricos:

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); }

Editar:

Escribí un código de ejemplo que compila una clase en tiempo de ejecución y luego intenta convertir a un objeto de dicha clase compilada. La salida fue como esperaba:

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); } } }

Editar 2:

Actualicé el código para probar su clase TestClassLoader - aún obtengo la versión correcta (actualizada) de la clase.


usted ha encontrado, solo puede usar TypeReference con tipos conocidos en tiempo de compilación (sin una meta-programación muy complicada).

Sin embargo, hay muchas sobrecargas alternativas para readValue que no requieren una TypeReference , para permitir casos como el suyo donde TypeReference es práctico.

Creo que puedes usar readValue(... , Class<T> valueType)

Si tiene un Classloader especial para estas clases compiladas recientemente, puede obtener una instancia de Class de eso y pasarlo, por ejemplo:

ClassLoader dynamicallyCompiledPojoLoader = ...; Class<?> pojoClass = dynamicallyCompiledPojoLoader.loadClass("..."); return mapper.readValue(..., pojoClass);

Consulte también com.fasterxml.jackson.databind.type.TypeFactory para especificar tipos genéricos parametrizados sin utilizar TypeReference

Actualización después de "Edit2: 24Aug17 Excepción enfrentada stackTrace está aquí"

Su excepción actual ( https://pastebin.com/ckCu2uWx ) no es un problema del cargador de clases, sino un problema de falta de correspondencia del esquema JSON.

La parte relevante del mensaje de excepción es:

Can not deserialize instance of java.util.ArrayList out of START_OBJECT token ... through reference chain: com.EnrichmentService.Thread72.EsRootDoc["category"]->java.util.ArrayList[0]->com.EnrichmentService.Thread72.Category["crawler"])

Entonces, Jackson no está contento de que el campo "rastreador" en el JSON sea un objeto, es decir, comience por " { ", pero el "rastreador" de la propiedad Java es ArrayList, es decir, debería comenzar por " [ "

No sé por qué la estructura POJO de Thread72.Category está mal aquí, pero no parece un problema de cargador de clases.

Actualice después de comentar que la clase POJO cambia con cada solicitud

Usted ha dicho que 1) la estructura de la clase POJO varía con cada solicitud y 2) que desea usar el mismo nombre de clase para el POJO cada vez.

Vea Java - ¿cómo cargar diferentes versiones de la misma clase?

Tendrá que usar un nuevo Classloader para cada solicitud, ya que las clases se almacenan en caché por Classloaders. El código que ha publicado hasta ahora sugiere que está utilizando un único cargador de clases y esperando una nueva carga de la clase "Categoría" en cada solicitud, lo que no funcionará.

Has dicho:

Necesito generar nuevas clases cada vez para un trabajo de reindexación de documentos elástica basado en drools y esta configuración de drools necesita pojo / tipo de objeto instances.thanks

... pero creo que deberías considerar el uso de un Mapa o una entrada similar a Drools en lugar de un POJO basado en la reflexión, dado que no conoces la estructura de antemano. Como ha encontrado aquí, este es un ajuste pobre para la abstracción clase / cargador de clases.

Ver por ej.