Merge pull request #9648 from eddumelendez:jsonb

* pr/9648:
  Polish "Add Jsonb support"
  Add Jsonb support
This commit is contained in:
Stephane Nicoll 2017-09-21 15:15:15 +02:00
commit 2e01d90660
24 changed files with 785 additions and 171 deletions

View File

@ -120,6 +120,11 @@
<artifactId>cache-api</artifactId> <artifactId>cache-api</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>javax.json.bind</groupId>
<artifactId>javax.json.bind-api</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>io.searchbox</groupId> <groupId>io.searchbox</groupId>
<artifactId>jest</artifactId> <artifactId>jest</artifactId>
@ -724,11 +729,21 @@
<artifactId>json-path</artifactId> <artifactId>json-path</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>javax.json</groupId>
<artifactId>javax.json-api</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.apache.johnzon</groupId>
<artifactId>johnzon-jsonb</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.hsqldb</groupId> <groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId> <artifactId>hsqldb</artifactId>

View File

@ -23,6 +23,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.NoneNestedConditions;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -33,6 +34,7 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConvert
* Configuration for HTTP Message converters that use Gson. * Configuration for HTTP Message converters that use Gson.
* *
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Eddú Meléndez
* @since 1.2.2 * @since 1.2.2
*/ */
@Configuration @Configuration
@ -41,7 +43,7 @@ class GsonHttpMessageConvertersConfiguration {
@Configuration @Configuration
@ConditionalOnBean(Gson.class) @ConditionalOnBean(Gson.class)
@Conditional(PreferGsonOrMissingJacksonCondition.class) @Conditional(PreferGsonOrJacksonAndJsonbUnavailableCondition.class)
protected static class GsonHttpMessageConverterConfiguration { protected static class GsonHttpMessageConverterConfiguration {
@Bean @Bean
@ -54,22 +56,41 @@ class GsonHttpMessageConvertersConfiguration {
} }
private static class PreferGsonOrMissingJacksonCondition extends AnyNestedCondition { private static class PreferGsonOrJacksonAndJsonbUnavailableCondition
extends AnyNestedCondition {
PreferGsonOrMissingJacksonCondition() { PreferGsonOrJacksonAndJsonbUnavailableCondition() {
super(ConfigurationPhase.REGISTER_BEAN); super(ConfigurationPhase.REGISTER_BEAN);
} }
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "gson", matchIfMissing = false) @ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "gson")
static class GsonPreferred { static class GsonPreferred {
} }
@ConditionalOnMissingBean(MappingJackson2HttpMessageConverter.class) @Conditional(JacksonAndJsonbUnavailable.class)
static class JacksonJsonbUnavailable {
}
}
private static class JacksonAndJsonbUnavailable extends NoneNestedConditions {
JacksonAndJsonbUnavailable() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnBean(MappingJackson2HttpMessageConverter.class)
static class JacksonMissing { static class JacksonMissing {
} }
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "jsonb")
static class JsonbPreferred {
}
} }
} }

View File

@ -26,6 +26,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration; import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -44,12 +45,15 @@ import org.springframework.http.converter.StringHttpMessageConverter;
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Eddú Meléndez
*/ */
@Configuration @Configuration
@ConditionalOnClass(HttpMessageConverter.class) @ConditionalOnClass(HttpMessageConverter.class)
@AutoConfigureAfter({ GsonAutoConfiguration.class, JacksonAutoConfiguration.class }) @AutoConfigureAfter({ GsonAutoConfiguration.class, JacksonAutoConfiguration.class,
JsonbAutoConfiguration.class })
@Import({ JacksonHttpMessageConvertersConfiguration.class, @Import({ JacksonHttpMessageConvertersConfiguration.class,
GsonHttpMessageConvertersConfiguration.class }) GsonHttpMessageConvertersConfiguration.class,
JsonbHttpMessageConvertersConfiguration.class })
public class HttpMessageConvertersAutoConfiguration { public class HttpMessageConvertersAutoConfiguration {
static final String PREFERRED_MAPPER_PROPERTY = "spring.http.converters.preferred-json-mapper"; static final String PREFERRED_MAPPER_PROPERTY = "spring.http.converters.preferred-json-mapper";

View File

@ -0,0 +1,78 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.http;
import javax.json.bind.Jsonb;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.GsonHttpMessageConverter;
import org.springframework.http.converter.json.JsonbHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
/**
* Configuration for HTTP Message converters that use JSON-B.
*
* @author Eddú Meléndez
* @author 2.0.0
*/
@Configuration
@ConditionalOnClass(Jsonb.class)
class JsonbHttpMessageConvertersConfiguration {
@Configuration
@ConditionalOnBean(Jsonb.class)
@Conditional(PreferJsonbOrMissingJacksonAndGsonCondition.class)
protected static class JsonbHttpMessageConverterConfiguration {
@Bean
@ConditionalOnMissingBean
public JsonbHttpMessageConverter jsonbHttpMessageConverter(Jsonb jsonb) {
JsonbHttpMessageConverter converter = new JsonbHttpMessageConverter();
converter.setJsonb(jsonb);
return converter;
}
}
private static class PreferJsonbOrMissingJacksonAndGsonCondition
extends AnyNestedCondition {
PreferJsonbOrMissingJacksonAndGsonCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "jsonb")
static class JsonbPreferred {
}
@ConditionalOnMissingBean({ MappingJackson2HttpMessageConverter.class, GsonHttpMessageConverter.class })
static class JacksonAndGsonMissing {
}
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.jsonb;
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link EnableAutoConfiguration Auto-configuration} for JSON-B.
*
* @author Eddú Meléndez
* @since 2.0.0
*/
@Configuration
@ConditionalOnClass(Jsonb.class)
public class JsonbAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public Jsonb jsonb() {
return JsonbBuilder.create();
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Auto-configuration for JSON-B.
*/
package org.springframework.boot.autoconfigure.jsonb;

View File

@ -200,7 +200,7 @@
{ {
"name": "spring.http.converters.preferred-json-mapper", "name": "spring.http.converters.preferred-json-mapper",
"type": "java.lang.String", "type": "java.lang.String",
"description": "Preferred JSON mapper to use for HTTP message conversion. Set to \"gson\" to force the use of Gson when both it and Jackson are on the classpath." "description": "Preferred JSON mapper to use for HTTP message conversion, auto-detected according to the environment by default."
}, },
{ {
"name": "spring.jersey.type", "name": "spring.jersey.type",
@ -1362,6 +1362,9 @@
}, },
{ {
"value": "jackson" "value": "jackson"
},
{
"value": "jsonb"
} }
], ],
"providers": [ "providers": [

View File

@ -80,6 +80,7 @@ org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\ org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\ org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\ org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\

View File

@ -16,28 +16,33 @@
package org.springframework.boot.autoconfigure.http; package org.springframework.boot.autoconfigure.http;
import java.util.Arrays; import javax.json.bind.Jsonb;
import java.util.List;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson; import com.google.gson.Gson;
import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration; import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration;
import org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration.MappingJackson2HttpMessageConverterConfiguration; import org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration.MappingJackson2HttpMessageConverterConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.boot.test.context.HidePackagesClassLoader;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration; import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
import org.springframework.hateoas.ResourceSupport; import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter; import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.GsonHttpMessageConverter; import org.springframework.http.converter.json.GsonHttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.JsonbHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
@ -51,191 +56,220 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author David Liu * @author David Liu
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @author Eddú Meléndez
*/ */
public class HttpMessageConvertersAutoConfigurationTests { public class HttpMessageConvertersAutoConfigurationTests {
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(
HttpMessageConvertersAutoConfiguration.class));
@After @Test
public void close() { public void jacksonNotAvailable() {
if (this.context != null) { this.contextRunner.run((context) -> {
this.context.close(); assertThat(context).doesNotHaveBean(ObjectMapper.class);
} assertThat(context).doesNotHaveBean(MappingJackson2HttpMessageConverter.class);
assertThat(context).doesNotHaveBean(MappingJackson2XmlHttpMessageConverter.class);
});
} }
@Test @Test
public void noObjectMapperMeansNoConverter() throws Exception { public void jacksonDefaultConverter() {
this.context.register(HttpMessageConvertersAutoConfiguration.class); this.contextRunner.withUserConfiguration(JacksonObjectMapperConfig.class)
this.context.refresh(); .run(assertConverter(MappingJackson2HttpMessageConverter.class,
assertThat(this.context.getBeansOfType(ObjectMapper.class)).isEmpty(); "mappingJackson2HttpMessageConverter"));
assertThat(this.context.getBeansOfType(MappingJackson2HttpMessageConverter.class))
.isEmpty();
assertThat(
this.context.getBeansOfType(MappingJackson2XmlHttpMessageConverter.class))
.isEmpty();
} }
@Test @Test
public void defaultJacksonConverter() throws Exception { public void jacksonConverterWithBuilder() {
this.context.register(JacksonObjectMapperConfig.class, this.contextRunner.withUserConfiguration(JacksonObjectMapperBuilderConfig.class)
HttpMessageConvertersAutoConfiguration.class); .run(assertConverter(MappingJackson2HttpMessageConverter.class,
this.context.refresh(); "mappingJackson2HttpMessageConverter"));
assertConverterBeanExists(MappingJackson2HttpMessageConverter.class,
"mappingJackson2HttpMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
MappingJackson2HttpMessageConverter.class);
} }
@Test @Test
public void defaultJacksonConvertersWithBuilder() throws Exception { public void jacksonXmlConverterWithBuilder() {
this.context.register(JacksonObjectMapperBuilderConfig.class, this.contextRunner.withUserConfiguration(JacksonObjectMapperBuilderConfig.class)
HttpMessageConvertersAutoConfiguration.class); .run(assertConverter(MappingJackson2XmlHttpMessageConverter.class,
this.context.refresh(); "mappingJackson2XmlHttpMessageConverter"));
assertConverterBeanExists(MappingJackson2HttpMessageConverter.class,
"mappingJackson2HttpMessageConverter");
assertConverterBeanExists(MappingJackson2XmlHttpMessageConverter.class,
"mappingJackson2XmlHttpMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
MappingJackson2HttpMessageConverter.class);
assertConverterBeanRegisteredWithHttpMessageConverters(
MappingJackson2XmlHttpMessageConverter.class);
} }
@Test @Test
public void customJacksonConverter() throws Exception { public void jacksonCustomConverter() {
this.context.register(JacksonObjectMapperConfig.class, this.contextRunner.withUserConfiguration(JacksonObjectMapperConfig.class,
JacksonConverterConfig.class, JacksonConverterConfig.class
HttpMessageConvertersAutoConfiguration.class); ).run(assertConverter(MappingJackson2HttpMessageConverter.class,
this.context.refresh(); "customJacksonMessageConverter"));
assertConverterBeanExists(MappingJackson2HttpMessageConverter.class,
"customJacksonMessageConverter");
} }
@Test @Test
public void noGson() throws Exception { public void gsonNotAvailable() {
this.context.register(HttpMessageConvertersAutoConfiguration.class); this.contextRunner.run((context) -> {
this.context.refresh(); assertThat(context).doesNotHaveBean(Gson.class);
assertThat(this.context.getBeansOfType(Gson.class).isEmpty()).isTrue(); assertThat(context).doesNotHaveBean(GsonHttpMessageConverter.class);
assertThat(this.context.getBeansOfType(GsonHttpMessageConverter.class).isEmpty()) });
.isTrue();
} }
@Test @Test
public void defaultGsonConverter() throws Exception { public void gsonDefaultConverter() {
this.context.register(GsonAutoConfiguration.class, this.contextRunner.withConfiguration(AutoConfigurations.of(
HttpMessageConvertersAutoConfiguration.class); GsonAutoConfiguration.class)
this.context.refresh(); ).run(assertConverter(GsonHttpMessageConverter.class,
assertConverterBeanExists(GsonHttpMessageConverter.class, "gsonHttpMessageConverter"));
"gsonHttpMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
GsonHttpMessageConverter.class);
} }
@Test @Test
public void jacksonIsPreferredByDefaultWhenBothGsonAndJacksonAreAvailable() { public void gsonCustomConverter() {
this.context.register(GsonAutoConfiguration.class, JacksonAutoConfiguration.class, this.contextRunner.withUserConfiguration(GsonConverterConfig.class)
HttpMessageConvertersAutoConfiguration.class); .withConfiguration(AutoConfigurations.of(GsonAutoConfiguration.class))
this.context.refresh(); .run(assertConverter(GsonHttpMessageConverter.class,
assertConverterBeanExists(MappingJackson2HttpMessageConverter.class, "customGsonMessageConverter"));
"mappingJackson2HttpMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
MappingJackson2HttpMessageConverter.class);
assertThat(this.context.getBeansOfType(GsonHttpMessageConverter.class)).isEmpty();
} }
@Test @Test
public void gsonCanBePreferredWhenBothGsonAndJacksonAreAvailable() { public void gsonCanBePreferred() {
this.context.register(GsonAutoConfiguration.class, JacksonAutoConfiguration.class, allOptionsRunner().withPropertyValues(
HttpMessageConvertersAutoConfiguration.class); "spring.http.converters.preferred-json-mapper:gson").run((context) -> {
TestPropertyValues.of("spring.http.converters.preferred-json-mapper:gson") assertConverterBeanExists(context, GsonHttpMessageConverter.class,
.applyTo(this.context); "gsonHttpMessageConverter");
this.context.refresh(); assertConverterBeanRegisteredWithHttpMessageConverters(context,
assertConverterBeanExists(GsonHttpMessageConverter.class, GsonHttpMessageConverter.class);
"gsonHttpMessageConverter"); assertThat(context).doesNotHaveBean(JsonbHttpMessageConverter.class);
assertConverterBeanRegisteredWithHttpMessageConverters( assertThat(context).doesNotHaveBean(MappingJackson2HttpMessageConverter.class);
GsonHttpMessageConverter.class); });
assertThat(this.context.getBeansOfType(MappingJackson2HttpMessageConverter.class))
.isEmpty();
} }
@Test @Test
public void customGsonConverter() throws Exception { public void jsonbNotAvailable() {
this.context.register(GsonAutoConfiguration.class, GsonConverterConfig.class, this.contextRunner.run((context) -> {
HttpMessageConvertersAutoConfiguration.class); assertThat(context).doesNotHaveBean(Jsonb.class);
this.context.refresh(); assertThat(context).doesNotHaveBean(JsonbHttpMessageConverter.class);
assertConverterBeanExists(GsonHttpMessageConverter.class, });
"customGsonMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
GsonHttpMessageConverter.class);
} }
@Test @Test
public void defaultStringConverter() throws Exception { public void jsonbDefaultConverter() {
this.context.register(HttpMessageConvertersAutoConfiguration.class); this.contextRunner.withConfiguration(AutoConfigurations.of(
this.context.refresh(); JsonbAutoConfiguration.class)
assertConverterBeanExists(StringHttpMessageConverter.class, ).run(assertConverter(JsonbHttpMessageConverter.class,
"stringHttpMessageConverter"); "jsonbHttpMessageConverter"));
assertConverterBeanRegisteredWithHttpMessageConverters(
StringHttpMessageConverter.class);
} }
@Test @Test
public void customStringConverter() throws Exception { public void jsonbCustomConverter() {
this.context.register(StringConverterConfig.class, this.contextRunner.withUserConfiguration(JsonbConverterConfig.class)
HttpMessageConvertersAutoConfiguration.class); .withConfiguration(AutoConfigurations.of(JsonbAutoConfiguration.class))
this.context.refresh(); .run(assertConverter(JsonbHttpMessageConverter.class,
assertConverterBeanExists(StringHttpMessageConverter.class, "customJsonbMessageConverter"));
"customStringMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
StringHttpMessageConverter.class);
} }
@Test @Test
public void typeConstrainedConverterDoesNotPreventAutoConfigurationOfJacksonConverter() public void jsonbCanBePreferred() {
throws Exception { allOptionsRunner().withPropertyValues(
this.context.register(JacksonObjectMapperBuilderConfig.class, "spring.http.converters.preferred-json-mapper:jsonb").run((context) -> {
TypeConstrainedConverterConfiguration.class, assertConverterBeanExists(context, JsonbHttpMessageConverter.class,
HttpMessageConvertersAutoConfiguration.class); "jsonbHttpMessageConverter");
this.context.refresh(); assertConverterBeanRegisteredWithHttpMessageConverters(context,
JsonbHttpMessageConverter.class);
BeanDefinition beanDefinition = this.context assertThat(context).doesNotHaveBean(GsonHttpMessageConverter.class);
.getBeanDefinition("mappingJackson2HttpMessageConverter"); assertThat(context).doesNotHaveBean(MappingJackson2HttpMessageConverter.class);
assertThat(beanDefinition.getFactoryBeanName()).isEqualTo( });
MappingJackson2HttpMessageConverterConfiguration.class.getName());
} }
@Test @Test
public void typeConstrainedConverterFromSpringDataDoesNotPreventAutoConfigurationOfJacksonConverter() public void stringDefaultConverter() {
throws Exception { this.contextRunner.run(assertConverter(StringHttpMessageConverter.class,
this.context.register(JacksonObjectMapperBuilderConfig.class, "stringHttpMessageConverter"));
RepositoryRestMvcConfiguration.class,
HttpMessageConvertersAutoConfiguration.class);
this.context.refresh();
BeanDefinition beanDefinition = this.context
.getBeanDefinition("mappingJackson2HttpMessageConverter");
assertThat(beanDefinition.getFactoryBeanName()).isEqualTo(
MappingJackson2HttpMessageConverterConfiguration.class.getName());
} }
private void assertConverterBeanExists(Class<?> type, String beanName) { @Test
assertThat(this.context.getBeansOfType(type)).hasSize(1); public void sringCustomConverter() {
List<String> beanNames = Arrays.asList(this.context.getBeanDefinitionNames()); this.contextRunner.withUserConfiguration(StringConverterConfig.class)
assertThat(beanNames).contains(beanName); .run(assertConverter(StringHttpMessageConverter.class,
"customStringMessageConverter"));
} }
private void assertConverterBeanRegisteredWithHttpMessageConverters(Class<?> type) { @Test
Object converter = this.context.getBean(type); public void typeConstrainedConverterDoesNotPreventAutoConfigurationOfJacksonConverter() {
HttpMessageConverters converters = this.context this.contextRunner.withUserConfiguration(JacksonObjectMapperBuilderConfig.class,
.getBean(HttpMessageConverters.class); TypeConstrainedConverterConfiguration.class).run((context) -> {
assertThat(converters.getConverters().contains(converter)).isTrue(); BeanDefinition beanDefinition = ((GenericApplicationContext) context.getSourceApplicationContext())
.getBeanDefinition("mappingJackson2HttpMessageConverter");
assertThat(beanDefinition.getFactoryBeanName()).isEqualTo(
MappingJackson2HttpMessageConverterConfiguration.class.getName());
});
}
@Test
public void typeConstrainedConverterFromSpringDataDoesNotPreventAutoConfigurationOfJacksonConverter() {
this.contextRunner.withUserConfiguration(JacksonObjectMapperBuilderConfig.class,
RepositoryRestMvcConfiguration.class).run((context) -> {
BeanDefinition beanDefinition = ((GenericApplicationContext) context.getSourceApplicationContext())
.getBeanDefinition("mappingJackson2HttpMessageConverter");
assertThat(beanDefinition.getFactoryBeanName()).isEqualTo(
MappingJackson2HttpMessageConverterConfiguration.class.getName());
});
}
@Test
public void jacksonIsPreferredByDefault() {
allOptionsRunner().run((context) -> {
assertConverterBeanExists(context, MappingJackson2HttpMessageConverter.class,
"mappingJackson2HttpMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(context,
MappingJackson2HttpMessageConverter.class);
assertThat(context).doesNotHaveBean(GsonHttpMessageConverter.class);
assertThat(context).doesNotHaveBean(JsonbHttpMessageConverter.class);
});
}
@Test
public void gsonIsPreferredIfJacksonIsNotAvailable() {
allOptionsRunner()
.withClassLoader(new HidePackagesClassLoader(
ObjectMapper.class.getPackage().getName())).run((context) -> {
assertConverterBeanExists(context, GsonHttpMessageConverter.class,
"gsonHttpMessageConverter");
assertThat(context).doesNotHaveBean(JsonbHttpMessageConverter.class);
});
}
@Test
public void jsonbIsPreferredIfJacksonAndGsonAreNotAvailable() {
allOptionsRunner()
.withClassLoader(new HidePackagesClassLoader(
ObjectMapper.class.getPackage().getName(),
Gson.class.getPackage().getName()))
.run(assertConverter(JsonbHttpMessageConverter.class,
"jsonbHttpMessageConverter"));
}
private ApplicationContextRunner allOptionsRunner() {
return this.contextRunner.withConfiguration(AutoConfigurations.of(
GsonAutoConfiguration.class, JacksonAutoConfiguration.class,
JsonbAutoConfiguration.class));
}
private ContextConsumer<AssertableApplicationContext> assertConverter(
Class<? extends HttpMessageConverter> converterType, String beanName) {
return context -> {
assertConverterBeanExists(context, converterType, beanName);
assertConverterBeanRegisteredWithHttpMessageConverters(context, converterType);
};
}
private void assertConverterBeanExists(AssertableApplicationContext context,
Class<?> type, String beanName) {
assertThat(context).hasSingleBean(type);
assertThat(context).hasBean(beanName);
}
private void assertConverterBeanRegisteredWithHttpMessageConverters(
AssertableApplicationContext context, Class<? extends HttpMessageConverter> type) {
HttpMessageConverter converter = context.getBean(type);
HttpMessageConverters converters = context.getBean(HttpMessageConverters.class);
assertThat(converters.getConverters()).contains(converter);
} }
@Configuration @Configuration
@ -288,6 +322,18 @@ public class HttpMessageConvertersAutoConfigurationTests {
} }
@Configuration
protected static class JsonbConverterConfig {
@Bean
public JsonbHttpMessageConverter customJsonbMessageConverter(Jsonb jsonb) {
JsonbHttpMessageConverter converter = new JsonbHttpMessageConverter();
converter.setJsonb(jsonb);
return converter;
}
}
@Configuration @Configuration
protected static class StringConverterConfig { protected static class StringConverterConfig {

View File

@ -0,0 +1,61 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.jsonb;
import javax.json.bind.Jsonb;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link JsonbAutoConfiguration}.
*
* @author Eddú Meléndez
*/
public class JsonbAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(JsonbAutoConfiguration.class));
@Test
public void jsonbRegistration() {
this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(Jsonb.class);
Jsonb jsonb = context.getBean(Jsonb.class);
assertThat(jsonb.toJson(new DataObject())).isEqualTo("{\"data\":\"hello\"}");
});
}
public class DataObject {
@SuppressWarnings("unused")
private String data = "hello";
public String getData() {
return this.data;
}
public void setData(String data) {
this.data = data;
}
}
}

View File

@ -118,8 +118,11 @@
<jna.version>4.4.0</jna.version> <jna.version>4.4.0</jna.version>
<joda-time.version>2.9.9</joda-time.version> <joda-time.version>2.9.9</joda-time.version>
<jolokia.version>1.3.7</jolokia.version> <jolokia.version>1.3.7</jolokia.version>
<johnzon-jsonb.version>1.1.2</johnzon-jsonb.version>
<jooq.version>3.9.5</jooq.version> <jooq.version>3.9.5</jooq.version>
<jsonassert.version>1.5.0</jsonassert.version> <jsonassert.version>1.5.0</jsonassert.version>
<javax-json.version>1.1</javax-json.version>
<javax-jsonb.version>1.0</javax-jsonb.version>
<json-path.version>2.4.0</json-path.version> <json-path.version>2.4.0</json-path.version>
<jstl.version>1.2</jstl.version> <jstl.version>1.2</jstl.version>
<jtds.version>1.3.1</jtds.version> <jtds.version>1.3.1</jtds.version>
@ -938,6 +941,16 @@
<artifactId>javax.jms-api</artifactId> <artifactId>javax.jms-api</artifactId>
<version>${javax-jms.version}</version> <version>${javax-jms.version}</version>
</dependency> </dependency>
<dependency>
<groupId>javax.json</groupId>
<artifactId>javax.json-api</artifactId>
<version>${javax-json.version}</version>
</dependency>
<dependency>
<groupId>javax.json.bind</groupId>
<artifactId>javax.json.bind-api</artifactId>
<version>${javax-jsonb.version}</version>
</dependency>
<dependency> <dependency>
<groupId>javax.mail</groupId> <groupId>javax.mail</groupId>
<artifactId>javax.mail-api</artifactId> <artifactId>javax.mail-api</artifactId>
@ -1305,6 +1318,11 @@
<artifactId>httpmime</artifactId> <artifactId>httpmime</artifactId>
<version>${httpclient.version}</version> <version>${httpclient.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.johnzon</groupId>
<artifactId>johnzon-jsonb</artifactId>
<version>${johnzon-jsonb.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.apache.logging.log4j</groupId> <groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-bom</artifactId> <artifactId>log4j-bom</artifactId>

View File

@ -297,7 +297,7 @@ content into your application; rather pick only the properties that you need.
spring.hateoas.use-hal-as-default-json-media-type=true # Specify if application/hal+json responses should be sent to requests that accept application/json. spring.hateoas.use-hal-as-default-json-media-type=true # Specify if application/hal+json responses should be sent to requests that accept application/json.
# HTTP message conversion # HTTP message conversion
spring.http.converters.preferred-json-mapper=jackson # Preferred JSON mapper to use for HTTP message conversion. Set to "gson" to force the use of Gson when both it and Jackson are on the classpath. spring.http.converters.preferred-json-mapper= # Preferred JSON mapper to use for HTTP message conversion, auto-detected according to the environment by default.
# HTTP encoding ({sc-spring-boot-autoconfigure}/http/HttpEncodingProperties.{sc-ext}[HttpEncodingProperties]) # HTTP encoding ({sc-spring-boot-autoconfigure}/http/HttpEncodingProperties.{sc-ext}[HttpEncodingProperties])
spring.http.encoding.charset=UTF-8 # Charset of HTTP requests and responses. Added to the "Content-Type" header if not set explicitly. spring.http.encoding.charset=UTF-8 # Charset of HTTP requests and responses. Added to the "Content-Type" header if not set explicitly.

View File

@ -5675,16 +5675,21 @@ TIP: It's also possible to use the `@AutoConfigure...` annotations with the stan
[[boot-features-testing-spring-boot-applications-testing-autoconfigured-json-tests]] [[boot-features-testing-spring-boot-applications-testing-autoconfigured-json-tests]]
==== Auto-configured JSON tests ==== Auto-configured JSON tests
To test that Object JSON serialization and deserialization is working as expected you can To test that Object JSON serialization and deserialization is working as expected you can
use the `@JsonTest` annotation. `@JsonTest` will auto-configure Jackson `ObjectMapper`, use the `@JsonTest` annotation. `@JsonTest` will auto-configure the available supported
any `@JsonComponent` beans and any Jackson `Modules`. It also configures `Gson` json mapper:
if you happen to be using that instead of, or as well as, Jackson. If you need to
configure elements of the auto-configuration you can use the `@AutoConfigureJsonTesters` * Jackson `ObjectMapper`, any `@JsonComponent` beans and any Jackson `Modules`
annotation. * `Gson`
* `Jsonb`
If you need to configure elements of the auto-configuration you can use the
`@AutoConfigureJsonTesters` annotation.
Spring Boot includes AssertJ based helpers that work with the JSONassert and JsonPath Spring Boot includes AssertJ based helpers that work with the JSONassert and JsonPath
libraries to check that JSON is as expected. The `JacksonTester`, `GsonTester` and libraries to check that JSON is as expected. The `JacksonTester`, `GsonTester`,
`BasicJsonTester` classes can be used for Jackson, Gson and Strings respectively. Any `JsonbTester` and `BasicJsonTester` classes can be used for Jackson, Gson, Jsonb and
helper fields on the test class can be `@Autowired` when using `@JsonTest`. Strings respectively. Any helper fields on the test class can be `@Autowired` when using
`@JsonTest`.
[source,java,indent=0] [source,java,indent=0]
---- ----
@ -6237,9 +6242,9 @@ A list of the auto-configuration that is enabled by `@DataLdapTest` can be
[[boot-features-testing-spring-boot-applications-testing-autoconfigured-rest-client]] [[boot-features-testing-spring-boot-applications-testing-autoconfigured-rest-client]]
==== Auto-configured REST clients ==== Auto-configured REST clients
The `@RestClientTest` annotation can be used if you want to test REST clients. By default The `@RestClientTest` annotation can be used if you want to test REST clients. By default
it will auto-configure Jackson and GSON support, configure a `RestTemplateBuilder` and it will auto-configure Jackson, GSON and Jsonb support, configure a `RestTemplateBuilder`
add support for `MockRestServiceServer`. The specific beans that you want to test should and add support for `MockRestServiceServer`. The specific beans that you want to test
be specified using `value` or `components` attribute of `@RestClientTest`: should be specified using `value` or `components` attribute of `@RestClientTest`:
[source,java,indent=0] [source,java,indent=0]

View File

@ -29,6 +29,11 @@
<artifactId>spring-boot-autoconfigure</artifactId> <artifactId>spring-boot-autoconfigure</artifactId>
</dependency> </dependency>
<!-- Optional --> <!-- Optional -->
<dependency>
<groupId>javax.json.bind</groupId>
<artifactId>javax.json.bind-api</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId> <artifactId>javax.servlet-api</artifactId>
@ -183,6 +188,11 @@
<artifactId>reactor-core</artifactId> <artifactId>reactor-core</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>javax.json</groupId>
<artifactId>javax.json-api</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId> <artifactId>commons-pool2</artifactId>
@ -208,6 +218,11 @@
<artifactId>jooq</artifactId> <artifactId>jooq</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.apache.johnzon</groupId>
<artifactId>johnzon-jsonb</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.mongodb</groupId> <groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-async</artifactId> <artifactId>mongodb-driver-async</artifactId>

View File

@ -28,6 +28,7 @@ import org.springframework.boot.test.autoconfigure.properties.PropertyMapping;
import org.springframework.boot.test.json.BasicJsonTester; import org.springframework.boot.test.json.BasicJsonTester;
import org.springframework.boot.test.json.GsonTester; import org.springframework.boot.test.json.GsonTester;
import org.springframework.boot.test.json.JacksonTester; import org.springframework.boot.test.json.JacksonTester;
import org.springframework.boot.test.json.JsonbTester;
/** /**
* Annotation that can be applied to a test class to enable and configure * Annotation that can be applied to a test class to enable and configure
@ -45,8 +46,8 @@ import org.springframework.boot.test.json.JacksonTester;
public @interface AutoConfigureJsonTesters { public @interface AutoConfigureJsonTesters {
/** /**
* If {@link BasicJsonTester}, {@link JacksonTester} and {@link GsonTester} beans * If {@link BasicJsonTester}, {@link JacksonTester}, {@link JsonbTester} and
* should be registered. Defaults to {@code true} * {@link GsonTester} beans should be registered. Defaults to {@code true}
* @return if tester support is enabled * @return if tester support is enabled
*/ */
boolean enabled() default true; boolean enabled() default true;

View File

@ -31,6 +31,7 @@ import org.springframework.boot.test.autoconfigure.filter.TypeExcludeFilters;
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper; import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
import org.springframework.boot.test.json.GsonTester; import org.springframework.boot.test.json.GsonTester;
import org.springframework.boot.test.json.JacksonTester; import org.springframework.boot.test.json.JacksonTester;
import org.springframework.boot.test.json.JsonbTester;
import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor; import org.springframework.core.annotation.AliasFor;
import org.springframework.test.context.BootstrapWith; import org.springframework.test.context.BootstrapWith;
@ -45,8 +46,9 @@ import org.springframework.test.context.BootstrapWith;
* {@code Module}) * {@code Module})
* <p> * <p>
* By default, tests annotated with {@code JsonTest} will also initialize * By default, tests annotated with {@code JsonTest} will also initialize
* {@link JacksonTester} and {@link GsonTester} fields. More fine-grained control can be * {@link JacksonTester}, {@link JsonbTester} and {@link GsonTester} fields. More
* provided via the {@link AutoConfigureJsonTesters @AutoConfigureJsonTesters} annotation. * fine-grained control can be provided via the {@link AutoConfigureJsonTesters @AutoConfigureJsonTesters}
* annotation.
* *
* @author Phillip Webb * @author Phillip Webb
* @see AutoConfigureJson * @see AutoConfigureJson

View File

@ -19,6 +19,8 @@ package org.springframework.boot.test.autoconfigure.json;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import javax.json.bind.Jsonb;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson; import com.google.gson.Gson;
@ -33,10 +35,12 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration; import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration;
import org.springframework.boot.test.json.AbstractJsonMarshalTester; import org.springframework.boot.test.json.AbstractJsonMarshalTester;
import org.springframework.boot.test.json.BasicJsonTester; import org.springframework.boot.test.json.BasicJsonTester;
import org.springframework.boot.test.json.GsonTester; import org.springframework.boot.test.json.GsonTester;
import org.springframework.boot.test.json.JacksonTester; import org.springframework.boot.test.json.JacksonTester;
import org.springframework.boot.test.json.JsonbTester;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
@ -48,13 +52,15 @@ import org.springframework.util.ReflectionUtils;
* Auto-configuration for Json testers. * Auto-configuration for Json testers.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Eddú Meléndez
* @see AutoConfigureJsonTesters * @see AutoConfigureJsonTesters
* @since 1.4.0 * @since 1.4.0
*/ */
@Configuration @Configuration
@ConditionalOnClass(name = "org.assertj.core.api.Assert") @ConditionalOnClass(name = "org.assertj.core.api.Assert")
@ConditionalOnProperty("spring.test.jsontesters.enabled") @ConditionalOnProperty("spring.test.jsontesters.enabled")
@AutoConfigureAfter({ JacksonAutoConfiguration.class, GsonAutoConfiguration.class }) @AutoConfigureAfter({ JacksonAutoConfiguration.class, GsonAutoConfiguration.class,
JsonbAutoConfiguration.class })
public class JsonTestersAutoConfiguration { public class JsonTestersAutoConfiguration {
@Bean @Bean
@ -94,6 +100,18 @@ public class JsonTestersAutoConfiguration {
} }
@ConditionalOnClass(Jsonb.class)
private static class JsonbJsonTesterConfiguration {
@Bean
@Scope("prototype")
@ConditionalOnBean(Jsonb.class)
public FactoryBean<JsonbTester<?>> jsonbTesterFactoryBean(Jsonb jsonb) {
return new JsonTesterFactoryBean<>(JsonbTester.class, jsonb);
}
}
/** /**
* {@link FactoryBean} used to create JSON Tester instances. * {@link FactoryBean} used to create JSON Tester instances.
*/ */

View File

@ -67,13 +67,15 @@ org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
# AutoConfigureJson auto-configuration imports # AutoConfigureJson auto-configuration imports
org.springframework.boot.test.autoconfigure.json.AutoConfigureJson=\ org.springframework.boot.test.autoconfigure.json.AutoConfigureJson=\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration
# AutoConfigureJsonTesters auto-configuration imports # AutoConfigureJsonTesters auto-configuration imports
org.springframework.boot.test.autoconfigure.json.AutoConfigureJsonTesters=\ org.springframework.boot.test.autoconfigure.json.AutoConfigureJsonTesters=\
org.springframework.boot.test.autoconfigure.json.JsonTestersAutoConfiguration,\ org.springframework.boot.test.autoconfigure.json.JsonTestersAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration
# AutoConfigureWebClient auto-configuration imports # AutoConfigureWebClient auto-configuration imports
org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient=\ org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient=\
@ -111,6 +113,7 @@ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\ org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\ org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration
@ -123,6 +126,7 @@ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\ org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\ org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\

View File

@ -28,6 +28,7 @@ import org.springframework.boot.test.json.BasicJsonTester;
import org.springframework.boot.test.json.GsonTester; import org.springframework.boot.test.json.GsonTester;
import org.springframework.boot.test.json.JacksonTester; import org.springframework.boot.test.json.JacksonTester;
import org.springframework.boot.test.json.JsonContent; import org.springframework.boot.test.json.JsonContent;
import org.springframework.boot.test.json.JsonbTester;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
@ -38,6 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* *
* @author Phillip Webb * @author Phillip Webb
* @author Madhura Bhave * @author Madhura Bhave
* @author Eddú Meléndez
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@JsonTest @JsonTest
@ -59,6 +61,9 @@ public class JsonTestIntegrationTests {
@Autowired @Autowired
private GsonTester<ExampleBasicObject> gsonJson; private GsonTester<ExampleBasicObject> gsonJson;
@Autowired
private JsonbTester<ExampleBasicObject> jsonbJson;
@Test @Test
public void basicJson() throws Exception { public void basicJson() throws Exception {
assertThat(this.basicJson.from("{\"a\":\"b\"}")).hasJsonPathStringValue("@.a"); assertThat(this.basicJson.from("{\"a\":\"b\"}")).hasJsonPathStringValue("@.a");
@ -84,6 +89,13 @@ public class JsonTestIntegrationTests {
assertThat(this.gsonJson.write(object)).isEqualToJson("example.json"); assertThat(this.gsonJson.write(object)).isEqualToJson("example.json");
} }
@Test
public void jsonb() throws Exception {
ExampleBasicObject object = new ExampleBasicObject();
object.setValue("spring");
assertThat(this.jsonbJson.write(object)).isEqualToJson("example.json");
}
@Test @Test
public void customView() throws Exception { public void customView() throws Exception {
ExampleJsonObjectWithView object = new ExampleJsonObjectWithView(); ExampleJsonObjectWithView object = new ExampleJsonObjectWithView();

View File

@ -25,6 +25,7 @@ import org.springframework.boot.test.autoconfigure.json.app.ExampleJsonApplicati
import org.springframework.boot.test.json.BasicJsonTester; import org.springframework.boot.test.json.BasicJsonTester;
import org.springframework.boot.test.json.GsonTester; import org.springframework.boot.test.json.GsonTester;
import org.springframework.boot.test.json.JacksonTester; import org.springframework.boot.test.json.JacksonTester;
import org.springframework.boot.test.json.JsonbTester;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
@ -50,6 +51,9 @@ public class JsonTestWithAutoConfigureJsonTestersTests {
@Autowired(required = false) @Autowired(required = false)
private GsonTester<ExampleBasicObject> gsonTester; private GsonTester<ExampleBasicObject> gsonTester;
@Autowired(required = false)
private JsonbTester<ExampleBasicObject> jsonbTester;
@Test @Test
public void basicJson() throws Exception { public void basicJson() throws Exception {
assertThat(this.basicJson).isNull(); assertThat(this.basicJson).isNull();
@ -65,4 +69,9 @@ public class JsonTestWithAutoConfigureJsonTestersTests {
assertThat(this.gsonTester).isNull(); assertThat(this.gsonTester).isNull();
} }
@Test
public void jsonb() throws Exception {
assertThat(this.jsonbTester).isNull();
}
} }

View File

@ -26,6 +26,7 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.json.BasicJsonTester; import org.springframework.boot.test.json.BasicJsonTester;
import org.springframework.boot.test.json.GsonTester; import org.springframework.boot.test.json.GsonTester;
import org.springframework.boot.test.json.JacksonTester; import org.springframework.boot.test.json.JacksonTester;
import org.springframework.boot.test.json.JsonbTester;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
@ -51,10 +52,14 @@ public class SpringBootTestWithAutoConfigureJsonTestersTests {
@Autowired @Autowired
private GsonTester<ExampleBasicObject> gsonTester; private GsonTester<ExampleBasicObject> gsonTester;
@Autowired
private JsonbTester<ExampleBasicObject> jsonbTester;
@Test @Test
public void contextLoads() { public void contextLoads() {
assertThat(this.basicJson).isNotNull(); assertThat(this.basicJson).isNotNull();
assertThat(this.jacksonTester).isNotNull(); assertThat(this.jacksonTester).isNotNull();
assertThat(this.jsonbTester).isNotNull();
assertThat(this.gsonTester).isNotNull(); assertThat(this.gsonTester).isNotNull();
} }

View File

@ -45,6 +45,11 @@
<artifactId>reactor-netty</artifactId> <artifactId>reactor-netty</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>javax.json.bind</groupId>
<artifactId>javax.json.bind-api</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId> <artifactId>javax.servlet-api</artifactId>
@ -116,6 +121,11 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<!-- Test --> <!-- Test -->
<dependency>
<groupId>javax.json</groupId>
<artifactId>javax.json-api</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test-support</artifactId> <artifactId>spring-boot-test-support</artifactId>
@ -142,6 +152,11 @@
<optional>true</optional> <optional>true</optional>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.apache.johnzon</groupId>
<artifactId>johnzon-jsonb</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.jetbrains.kotlin</groupId> <groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-runtime</artifactId> <artifactId>kotlin-runtime</artifactId>

View File

@ -0,0 +1,128 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.json;
import java.io.IOException;
import java.io.Reader;
import javax.json.bind.Jsonb;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.core.ResolvableType;
import org.springframework.util.Assert;
/**
* AssertJ based JSON tester backed by Jsonb. Usually instantiated via
* {@link #initFields(Object, Jsonb)}, for example: <pre class="code">
* public class ExampleObjectJsonTests {
*
* private JsonbTester&lt;ExampleObject&gt; json;
*
* &#064;Before
* public void setup() {
* Jsonb jsonb = JsonbBuilder.create();
* JsonbTester.initFields(this, jsonb);
* }
*
* &#064;Test
* public void testWriteJson() throws IOException {
* ExampleObject object = // ...
* assertThat(json.write(object)).isEqualToJson(&quot;expected.json&quot;);
* }
*
* }
* </pre>
*
* See {@link AbstractJsonMarshalTester} for more details.
*
* @param <T> the type under test
* @author Eddú Meléndez
* @since 2.0.0
*/
public class JsonbTester<T> extends AbstractJsonMarshalTester<T> {
private final Jsonb jsonb;
/**
* Create a new uninitialized {@link JsonbTester} instance.
* @param jsonb the Jsonb instance
*/
protected JsonbTester(Jsonb jsonb) {
Assert.notNull(jsonb, "Jsonb must not be null");
this.jsonb = jsonb;
}
/**
* Create a new {@link JsonbTester} instance.
* @param resourceLoadClass the source class used to load resources
* @param type the type under test
* @param jsonb the Jsonb instance
* @see #initFields(Object, Jsonb)
*/
public JsonbTester(Class<?> resourceLoadClass, ResolvableType type, Jsonb jsonb) {
super(resourceLoadClass, type);
Assert.notNull(jsonb, "Jsonb must not be null");
this.jsonb = jsonb;
}
@Override
protected String writeObject(T value, ResolvableType type) throws IOException {
return this.jsonb.toJson(value, type.getType());
}
@Override
protected T readObject(Reader reader, ResolvableType type) throws IOException {
return this.jsonb.fromJson(reader, type.getType());
}
/**
* Utility method to initialize {@link JsonbTester} fields. See {@link JsonbTester
* class-level documentation} for example usage.
* @param testInstance the test instance
* @param jsonb the Jsonb instance
*/
public static void initFields(Object testInstance, Jsonb jsonb) {
new JsonbFieldInitializer().initFields(testInstance, jsonb);
}
/**
* Utility method to initialize {@link JsonbTester} fields. See {@link JsonbTester
* class-level documentation} for example usage.
* @param testInstance the test instance
* @param jsonb an object factory to create the Jsonb instance
*/
public static void initFields(Object testInstance, ObjectFactory<Jsonb> jsonb) {
new JsonbTester.JsonbFieldInitializer().initFields(testInstance, jsonb);
}
/**
* {@link FieldInitializer} for Jsonb.
*/
private static class JsonbFieldInitializer extends FieldInitializer<Jsonb> {
protected JsonbFieldInitializer() {
super(JsonbTester.class);
}
@Override
protected AbstractJsonMarshalTester<Object> createTester(
Class<?> resourceLoadClass, ResolvableType type, Jsonb marshaller) {
return new JsonbTester<>(resourceLoadClass, type, marshaller);
}
}
}

View File

@ -0,0 +1,89 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.json;
import java.util.List;
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import org.junit.Test;
import org.springframework.core.ResolvableType;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link JsonbTester}.
*
* @author Eddú Meléndez
*/
public class JsonbTesterTests extends AbstractJsonMarshalTesterTests {
@Test
public void initFieldsWhenTestIsNullShouldThrowException() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("TestInstance must not be null");
JsonbTester.initFields(null, JsonbBuilder.create());
}
@Test
public void initFieldsWhenMarshallerIsNullShouldThrowException() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Marshaller must not be null");
JsonbTester.initFields(new InitFieldsTestClass(), (Jsonb) null);
}
@Test
public void initFieldsShouldSetNullFields() {
InitFieldsTestClass test = new InitFieldsTestClass();
assertThat(test.test).isNull();
assertThat(test.base).isNull();
JsonbTester.initFields(test, JsonbBuilder.create());
assertThat(test.test).isNotNull();
assertThat(test.base).isNotNull();
assertThat(test.test.getType().resolve()).isEqualTo(List.class);
assertThat(test.test.getType().resolveGeneric()).isEqualTo(ExampleObject.class);
}
@Override
protected AbstractJsonMarshalTester<Object> createTester(Class<?> resourceLoadClass,
ResolvableType type) {
return new JsonbTester<>(resourceLoadClass, type, JsonbBuilder.create());
}
static abstract class InitFieldsBaseClass {
public JsonbTester<ExampleObject> base;
public JsonbTester<ExampleObject> baseSet = new JsonbTester<>(
InitFieldsBaseClass.class, ResolvableType.forClass(ExampleObject.class),
JsonbBuilder.create());
}
static class InitFieldsTestClass extends InitFieldsBaseClass {
public JsonbTester<List<ExampleObject>> test;
public JsonbTester<ExampleObject> testSet = new JsonbTester<>(
InitFieldsBaseClass.class, ResolvableType.forClass(ExampleObject.class),
JsonbBuilder.create());
}
}