Jackson deserialisation / TypeReference用于动态加载的pojo类 [英] Jackson deserialisation/TypeReference for dynamically loaded pojo class

查看:348
本文介绍了Jackson deserialisation / TypeReference用于动态加载的pojo类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要输入 JSON 输入Pojo实例,我使用的是 Jackson 2 库以及 readValue 方法可以使用typeReferencing反序列化:

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

但问题是因为 POJO 是在运行时动态创建和加载,如何获得 JSON POJO 实例/对象,因为我没有完全限定上述语句的类(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?

注意:我使用 jsonSchema2pojo 库生成 POJO 运行时的类。

Note: I use jsonSchema2pojo library to generate POJO classes at runtime.

这是代码片段,我用来生成 POJO for $ code> JSON 在运行时
并尝试

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

但由于尚未生成 com.EnrichmentService.Thread72.EsRootDoc ,编译器会在找不到类时出错。

But as com.EnrichmentService.Thread72.EsRootDoc has yet not been generated compiler would error to class not Found.

要点:

1)相同的Pojo类在运行时迭代生成但具有不同的属性,因为每次JSON输入都会更改。

1) Same Pojo classes generated at run time iteratively but with different properties as JSON input changes each time.

2)甚至尝试了
Object pojo = mapper.readValue(json,Class.forName(com.EnrichmentService.Thread72.EsRootDoc));因为class.forName不会替换现有的类!

2) Even tried Object pojo =mapper.readValue(json,Class.forName("com.EnrichmentService.Thread72.EsRootDoc")); as class.forName does not replace an existing class!

编辑8月23日 - 这是我的自定义类加载器:

Edit 24 Aug17 - Here is my custom class loader :

注意:Indexer是在运行时加载动态EsRootDoc / POJO类的类。

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

我尝试使用上面的TestClassLoader自定义类加载器作为替代方式是这样的:

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.

提到旧答案@ 如何在java中正在运行的应用程序中替换类?

编辑2:24Aug17
遇到异常stackTrace就在这里: https: //pastebin.com/ckCu2uWx

推荐答案

你找到了,你只能使用 TypeReference 包含在编译时已知的类型(没有一些非常棘手的元编程)。

you have found, you can only use TypeReference with types that are known at compile time (without some very tricky meta-programming).

但是,有很多替代重载 readValue ,不需要 TypeReference ,以允许像你这样的情况 TypeReference 是不切实际的。

However, there are lots of alternative overloads to readValue which do not require a TypeReference, to allow for cases like yours where TypeReference is impractical.

我认为你可以使用 readValue(...,Class< T> valueType)

I think you can use readValue(... , Class<T> valueType)

如果您有这些后期编译类的特殊类加载器,那么您可以得到 Class 实例并将其传入,例如:

If you have a special Classloader for these late-compiled classes, then you can get a Class instance from that and pass it in, for example:

ClassLoader dynamicallyCompiledPojoLoader = ...;
Class<?> pojoClass = dynamicallyCompiledPojoLoader.loadClass("...");

return mapper.readValue(..., pojoClass);

参见 com.fasterxml.jackson.databind.type.TypeFactory ,用于指定参数化泛型类型而不使用 TypeReference

See also com.fasterxml.jackson.databind.type.TypeFactory for specifying parameterised generic types without using TypeReference

您当前的例外( https://pastebin.com/ckCu2uWx )不是类加载器问题,但JSON模式不匹配问题。

Your current exception ( https://pastebin.com/ckCu2uWx ) is not a class loader issue, but a JSON schema mismatch issue.

异常消息的相关部分是:

The relevant part of the exception message is:

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"])

所以杰克逊不高兴JSON中的crawler字段是一个对象,即以 {开头,但Java属性crawler是一个ArrayList,即应该以<$开头 c $ c> [

So Jackson is unhappy that the "crawler" field in the JSON is an object, i.e. starts with "{", but the Java property "crawler" is an ArrayList, i.e. should start with "["

我不知道为什么POJO结构 Thread72.Category 在这里是错误的,但它看起来不像是一个类加载器问题。

I don't know why the POJO structure of Thread72.Category is wrong here, but it doesn't look like a classloader problem.

您已经说过1)POJO类结构因每个请求而异,2)您希望每次都为POJO使用相同的类名。

You have said that 1) the POJO class structure varies with each request and 2) you want to use the same classname for the POJO each time.

Java - 如何加载同一类的不同版本?

您需要为每个请求使用新的类加载器,因为类会被类加载器缓存。到目前为止您发布的代码表明您正在使用单个类加载器并希望在每个请求上加载类别类,这将无效。

You'll need to use a new Classloader for each request, as classes get cached by Classloaders. The code you have posted so far suggests that you are using a single classloader and hoping for a new load of the "Category" class on each request, which won't work.

你说过:


我需要每次为基于drools的elasticsearch文档重新索引工作生成新类,这个drools设置需要pojo /对象类型instances.thanks

I need to generate new classes each time for a drools based elasticsearch document re-indexing work and this drools setup needs pojo/Object type instances.thanks

...但我认为你应该考虑使用地图或类似输入到Drools而不是鉴于您事先并不知道结构,而不是基于反射的POJO。正如你在这里找到的那​​样,这不适合类/类加载器抽象。

... but I think you should look into using a Map or similar input to Drools rather than a reflection based POJO, given that you don't know the structure in advance. As you have found here, this is a poor fit for the class / classloader abstraction.

参见例如

  • https://groups.google.com/forum/#!topic/drools-usage/CbuSO-V-w_g
  • Convert Java POJO to Drools DRL and vice versa
  • https://groups.google.com/forum/#!topic/drools-usage/0BIXF3Tg5pw

这篇关于Jackson deserialisation / TypeReference用于动态加载的pojo类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆