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

JSON ObjectMapper with javac "-parameters" behaves when run via maven, not via InteliJ IDEA

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

首先,我的目标:

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

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

我有这个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>

我用它来编译 运行 下面的小 self-contained 程序:

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

事情是这样的:

所以,这是我的问题:

编辑

抱歉,assert 离子不一定能证明任何事情,因为它们不一定与 -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: 参数,因此可以忽略此块。第二个是将 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 中获取 运行 源代码的唯一方法是在编译器设置(不可移植)的 "Additional command line parameters" 字段中自行启用标志,或者通过使用 maven 编译作为 运行 配置中的预制作步骤。如果您希望在 IntelliJ 中自动执行此操作,您可能必须向 Jetbrains 提出问题。