jjzjj

java - 动态加载的 pojo 类的 Jackson 反序列化/TypeReference

coder 2023-09-02 原文

我需要获取 JSON 输入 Pojo 实例,我正在使用 Jackson 2 库,下面的 readValue 方法可以使用 typeReferencing 反序列化:

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

但问题是,由于 POJO 是在运行时动态创建和加载的,我如何将 JSON 获取到 POJO 实例/对象我没有上述声明的完全限定类 (POJO_ClassName) 名称?

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

这是代码片段,我用它在运行时为 JSON 生成 POJO 并尝试

  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 尚未生成,编译器将错误为未找到类。

要点:

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

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

Edit 24 Aug17 - 这是我的自定义类加载器:

注意: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 自定义类加载器作为替代方法是这样的:

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.

引用旧答案@ How to replace classes in a running application in java ?

Edit2:24Aug17 stackTrace 面临的异常在这里:https://pastebin.com/ckCu2uWx

最佳答案

我有两种方法可以解决这个问题:

  1. 在编译时创建和编译类(例如使用 maven 和 jaxb)

  1. 你做这样的事情:

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

如果该代码在 mapper.readValue() 之前失败,您将遇到另一个问题(我猜是类加载)。

更好的是使用泛型:

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

编辑:

我写了一些示例代码,在运行时编译一个类,然后尝试转换为所述编译类的对象。输出如我所料:

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

编辑 2:

更新代码以尝试您的 TestClassLoader 类 - 仍然获得该类的正确(更新)版本。

关于java - 动态加载的 pojo 类的 Jackson 反序列化/TypeReference,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45518549/

有关java - 动态加载的 pojo 类的 Jackson 反序列化/TypeReference的更多相关文章

  1. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  2. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  3. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  4. ruby-on-rails - 使用 config.threadsafe 时从 lib/加载模块/类的正确方法是什么!选项? - 2

    我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co

  5. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  6. 没有类的 Ruby 方法? - 2

    大家好!我想知道Ruby中未使用语法ClassName.method_name调用的方法是如何工作的。我头脑中的一些是puts、print、gets、chomp。可以在不使用点运算符的情况下调用这些方法。为什么是这样?他们来自哪里?我怎样才能看到这些方法的完整列表? 最佳答案 Kernel中的所有方法都可用于Object类的所有对象或从Object派生的任何类。您可以使用Kernel.instance_methods列出它们。 关于没有类的Ruby方法?,我们在StackOverflow

  7. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  8. ruby - Rails 关联 - 同一个类的多个 has_one 关系 - 2

    我的问题的一个例子是体育游戏。一场体育比赛有两支球队,一支主队和一支客队。我的事件记录模型如下:classTeam"Team"has_one:away_team,:class_name=>"Team"end我希望能够通过游戏访问一个团队,例如:Game.find(1).home_team但我收到一个单元化常量错误:Game::team。谁能告诉我我做错了什么?谢谢, 最佳答案 如果Gamehas_one:team那么Rails假设您的teams表有一个game_id列。不过,您想要的是games表有一个team_id列,在这种情况下

  9. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  10. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

随机推荐