2016-10-21 12 views
1

Как вы можете, вероятно, собрать из названия, это несколько сложная проблема.JSON ObjectMapper с javac «-параметрами» ведет себя при запуске через maven, а не через InteliJ IDEA

Прежде всего, моя цель:

  • Я пытаюсь добиться преобразования моих классов Java и из JSON без добавления каких-либо конкретных JSon аннотаций к ним.

  • Мои классы Java включают immutables, которые должны инициализации своих членов от параметров, передаваемых в конструктор, так что я должен иметь несколько параметров конструкторов, которые работают без @JsonCreator и без @JsonParameter.

  • Я использую Jackson ObjectMapper. Если есть другой объект ObjectMapper, который я могу использовать, который работает без описанной здесь проблемы, я был бы рад использовать его, но он должен был бы быть не менее авторитетным как Jackson ObjectMapper. (Так что, я не хочу, чтобы загрузить ObjectMapper Джима от его GitHub.)

Мое понимание того, как это на самом деле может быть достигнуто, в случае, если я ошибаюсь где-то:

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, она работает fi ne и отображает «Успех!».

  • Если я делаю «перестроить проект» изнутри Intellij Idea, а затем запускаю/отлаживает, он не работает с JsonMappingException, что «подходящий конструктор не найден для простого типа jsontest.MyMain».

  • Странная вещь (для меня) - это код перед выполнением экземпляра ObjectMapper, чтобы убедиться, что имена параметров конструктора присутствуют и действительны, эффективно гарантируя, что аргумент «-параметров» успешно передан в компилятор, , и эти проверки всегда проходят!

  • Если я изменить мою «конфигурацию отладки» в идее, а в разделе «Перед запуском» я удалить «Сделать», и я заменить его на «Run Maven цели» compile то я могу успешно запустить свою программу из идеи , но Я не хочу этого делать. (Кроме того, он не работает очень хорошо, я думаю, что я должен делать что-то неправильно: нередко я запускаю, и он терпит неудачу с тем же исключением, что и выше, и при следующем запуске он преуспевает.)

Итак, вот мои вопросы:

  • Почему моя программа ведет себя по-разному, когда составитель мавена, чем при компиляции с идеей?

    • Более конкретно: что проблема ObjectMapper дал, что мои утверждения доказывают, что «аргумент»-параметров был передается компилятору, и аргументы сделать есть имена?
  • Что я могу сделать, чтобы идея собрать свою программу так же, как мавена (по крайней мере, по отношению к рассматриваемой задаче) без замены Идея в «Make»?

  • Почему это не работает последовательно, когда я заменяю по умолчанию «Make» на «Run maven goal» compile в конфигурации отладки Idea? (Что я делаю неправильно?)

EDIT

Мои извинения, ионы assert были не обязательно доказывать что-либо, так как они не обязательно включена -enableassertions. Я заменил их if() throw RuntimeException(), чтобы избежать путаницы.

+0

У вас есть флаг '-parameters', введенный в поле« Дополнительные параметры командной строки: »в настройках IDEA (Build, Execution -> Compiler -> Java Compiler)? Изменяется ли поведение, когда вы это делаете или нет? – Barend

+0

@Barend хороший вопрос, спасибо. Я забыл упомянуть об этом в вопросе. Да, если я добавлю «-параметры» в поле «Дополнительные параметры командной строки:», то он будет работать. Но, как вы уже догадались, я не хочу этого делать. –

+0

Боюсь, что у меня больше нет ничего. Я думаю, что это может быть для JetBrains. – Barend

ответ

5

Как я могу видеть в 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 являются включение самого флажка в поле «Дополнительные параметры командной строки» параметров компилятора (которое не переносимо) или путем компиляции с помощью maven как предварительный шаг в вашей конфигурации запуска. Вероятно, вам придется записывать проблему с Jetbrains, если вы хотите, чтобы это было возможно в IntelliJ автоматически.

 Смежные вопросы

  • Нет связанных вопросов^_^