Как вы можете, вероятно, собрать из названия, это несколько сложная проблема.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()
, чтобы избежать путаницы.
У вас есть флаг '-parameters', введенный в поле« Дополнительные параметры командной строки: »в настройках IDEA (Build, Execution -> Compiler -> Java Compiler)? Изменяется ли поведение, когда вы это делаете или нет? – Barend
@Barend хороший вопрос, спасибо. Я забыл упомянуть об этом в вопросе. Да, если я добавлю «-параметры» в поле «Дополнительные параметры командной строки:», то он будет работать. Но, как вы уже догадались, я не хочу этого делать. –
Боюсь, что у меня больше нет ничего. Я думаю, что это может быть для JetBrains. – Barend