java - 带有 javac "-parameters"的 JSON ObjectMapper 在通过 maven 而不是通过 IntelliJ IDEA 运行时的行为

标签 java json maven intellij-idea javac

正如您可能从标题中得知的那样,这是一个有点复杂的问题。

首先,我的目标:

  • 我正在尝试实现我的 java 类与 JSON 之间的转换 无需向它们添加任何特定于 json 的注释。
  • 我的 java 类包括不可变对象(immutable对象),它必须从传递给构造函数的参数初始化它们的成员,所以我必须 具有有效的多参数构造函数没有@JsonCreator 和没有@JsonParameter。
  • 我正在使用 jackson ObjectMapper。如果有另一个我可以使用的 ObjectMapper 可以在没有此处描述的问题的情况下工作,我很乐意使用它,但它必须与 jackson ObjectMapper 一样享有盛誉。 (所以,我不愿意从 Jim 的 GitHub 下载 ObjectMapper。)

  • 我对如何实际实现这一点的理解,以防我在某处错了:

    Java 用于使方法(和构造函数)参数类型可通过反射发现,但不能通过参数名称发现。这就是为什么 @JsonCreator 和 @JsonParameter 注释曾经是必要的:告诉 json ObjectMapper 哪个构造函数参数对应于哪个属性。在 Java 8 中,如果您提供新的 -parameters,编译器会将方法(和构造函数)参数名称发送到字节码中。参数,并将通过反射使它们可用,并且 jackson ObjectMapper 的最新版本支持这一点,因此现在应该可以在没有任何 json 特定注释的情况下进行 json 对象映射。

    我有这个 pom.xml:
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>test</groupId>
        <artifactId>test.json</artifactId>
        <version>1.0-SNAPSHOT</version>
        <name>Json Test</name>
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
        <build>
            <sourceDirectory>main</sourceDirectory>
            <testSourceDirectory>test</testSourceDirectory>
            <plugins>
                <plugin>
                    <!--<groupId>org.apache.maven.plugins</groupId>-->
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.5</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <!--<compilerArgument>-parameters</compilerArgument>-->
                        <!--<fork>true</fork>-->
                        <compilerArgs>
                            <arg>-parameters</arg>
                        </compilerArgs>
                    </configuration>
                </plugin>
            </plugins>
        </build>
        <dependencies>
            <dependency>
                <groupId>com.fasterxml.jackson.jaxrs</groupId>
                <artifactId>jackson-jaxrs-json-provider</artifactId>
                <version>2.7.2</version>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.module</groupId>
                <artifactId>jackson-module-parameter-names</artifactId>
                <version>2.7.2</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.11</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </project>
    

    我用它来编译和运行以下独立的小程序:
    package jsontest;
    
    import com.fasterxml.jackson.annotation.*;
    import com.fasterxml.jackson.databind.*;
    import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
    
    import java.io.IOException;
    import java.lang.reflect.*;
    
    public final class MyMain
    {
        public static void main( String[] args ) throws IOException, NoSuchMethodException
        {
            Method m = MyMain.class.getMethod("main", String[].class);
            Parameter mp = m.getParameters()[0];
            if( !mp.isNamePresent() || !mp.getName().equals("args") )
                throw new RuntimeException();
            Constructor<MyMain> c = MyMain.class.getConstructor(String.class,String.class);
            Parameter m2p0 = c.getParameters()[0];
            if( !m2p0.isNamePresent() || !m2p0.getName().equals("s1") )
                throw new RuntimeException();
            Parameter m2p1 = c.getParameters()[1];
            if( !m2p1.isNamePresent() || !m2p1.getName().equals("s2") )
                throw new RuntimeException();
    
            ObjectMapper mapper = new ObjectMapper();
            mapper.registerModule( new ParameterNamesModule() ); // "-parameters" option must be passed to the java compiler for this to work.
            mapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true );
            mapper.configure( SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true );
            mapper.setSerializationInclusion( JsonInclude.Include.ALWAYS );
            mapper.setVisibility( PropertyAccessor.ALL, JsonAutoDetect.Visibility.PUBLIC_ONLY );
            mapper.enableDefaultTyping( ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY );
    
            MyMain t = new MyMain( "1", "2" );
            String json = mapper.writeValueAsString( t );
            /*
             * Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class saganaki.Test]: can not
             * instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
             */
            t = mapper.readValue( json, MyMain.class );
            if( !t.s1.equals( "1" ) || !t.s2.equals( "2" ) )
                throw new RuntimeException();
            System.out.println( "Success!" );
        }
    
        public final String s1;
        public final String s2;
    
        public MyMain( String s1, String s2 )
        {
            this.s1 = s1;
            this.s2 = s2;
        }
    }
    

    这是发生的事情:
  • 如果我使用 mvn clean compile 编译程序然后从 Idea 中运行或调试它,它工作正常并显示“成功!”。
  • 如果我在 Intellij Idea 中执行“重建项目”,然后运行/调试,它会失败并显示 JsonMappingException “没有为简单类型 jsontest.MyMain 找到合适的构造函数”。
  • (对我来说)奇怪的是,ObjectMapper 实例化之前的代码检查以确保构造函数参数名称存在且有效,有效地确保“-parameters”参数已成功传递给编译器,并且这些检查总是通过!
  • 如果我在 Idea 和“启动前”部分编辑我的“调试配置”,我会删除“Make”并将其替换为“Run maven goal”compile然后我可以在 Idea 中成功运行我的程序,但是 我不想做必须这样做。 (另外,它甚至不能很好地工作,我想我一定是做错了什么:我经常运行它并失败并出现与上述相同的异常,而下一次运行它会成功。)

  • 所以,这是我的问题:
  • 为什么我的程序在使用 maven 编译时与使用 Idea 编译时的行为不同?
  • 更具体地说:鉴于我的断言证明“-parameters”参数已传递给编译器,并且参数确实有名称,ObjectMapper 的问题是什么?
  • 我该怎么做才能让 Idea 以与 maven 相同的方式编译我的程序(至少就手头的问题而言)而不替换 Idea 的“Make”?
  • 为什么当我将默认的“Make”替换为“Run maven goal”compile 时,它不能始终如一地工作在Idea的调试配置中? (我做错了什么?)

  • 编辑

    抱歉,assert ionic 不一定能证明什么,因为它们不一定是通过 -enableassertions 启用的。 .我将它们替换为 if() throw RuntimeException()以避免混淆。

    最佳答案

    据我所见 IntelliJ Community edition sources , IntelliJ 没有对 compilerArgs 做任何事情你正在指定。

    MavenProject.java ,有两个地方compilerArgs正在阅读:

    Element compilerArguments = compilerConfiguration.getChild("compilerArgs");
    if (compilerArguments != null) {
      for (Element element : compilerArguments.getChildren()) {
        String arg = element.getValue();
        if ("-proc:none".equals(arg)) {
          return ProcMode.NONE;
        }
        if ("-proc:only".equals(arg)) {
          return ProcMode.ONLY;
        }
      }
    }
    


    Element compilerArgs = compilerConfig.getChild("compilerArgs");
    if (compilerArgs != null) {
      for (Element e : compilerArgs.getChildren()) {
        if (!StringUtil.equals(e.getName(), "arg")) continue;
        String arg = e.getTextTrim();
        addAnnotationProcessorOption(arg, res);
      }
    }
    

    第一个代码块只查看 -proc:参数,因此可以忽略此 block 。第二个是传递 arg 的值addAnnotationProcessorOption 的元素(您正在指定)方法。
    private static void addAnnotationProcessorOption(String compilerArg, Map<String, String> optionsMap) {
      if (compilerArg == null || compilerArg.trim().isEmpty()) return;
    
      if (compilerArg.startsWith("-A")) {
        int idx = compilerArg.indexOf('=', 3);
        if (idx >= 0) {
          optionsMap.put(compilerArg.substring(2, idx), compilerArg.substring(idx + 1));
        } else {
          optionsMap.put(compilerArg.substring(2), "");
        }
      }
    }
    

    此方法仅处理以 -A 开头的参数,用于将选项传递给注释处理器。其他参数被忽略。

    目前,让您的源代码在 IntelliJ 中运行的唯一方法是在编译器设置(不可移植)的“附加命令行参数”字段中自己启用标志,或者使用 maven 编译作为预在您的运行配置中迈出一步。如果您希望在 IntelliJ 中自动执行此操作,您可能必须向 Jetbrains 提出问题。

    关于java - 带有 javac "-parameters"的 JSON ObjectMapper 在通过 maven 而不是通过 IntelliJ IDEA 运行时的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40170001/

    相关文章:

    java - 任何版本的 maven-shade-plugin 是否适用于 Maven 2.2.1?

    java - 如何在J2ME中读取和解析PHP对象

    maven - 使用 TestNG 运行 Selenium webdriver 脚本时,未使用 mvn clean install 命令创建测试输出文件夹

    java - 用于解析简单的基于文本的数据文件的正则表达式

    java - HBase FuzzyRowFilter 不返回任何结果

    javascript - meteor js |通过助手在 View 中显示 Json

    java - 休息问题 使用rest easy的服务

    javascript - 从事件中提取 JSON 对象

    maven - 如何将 Maven 配置为默认使用 JDK6,但根据需要使用 JDK7

    java - 为什么 maven-war-plugin 忽略我配置的 Web 资源过滤器?