Add Jsonb support

Spring Framework 5 will support Jsonb as a HttpMessageConverter, this
commit adds auto-configuration support. Also, support for Jsonb in the
@JsonTest has been added.

This implementation is running against Apache Johnzon

See gh-9648
pull/9635/head
Eddú Meléndez 8 years ago committed by Stephane Nicoll
parent 676dec8cf7
commit 97aeaa4a32

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

@ -23,6 +23,7 @@ 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.boot.autoconfigure.condition.NoneNestedConditions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
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.
*
* @author Andy Wilkinson
* @author Eddú Meléndez
* @since 1.2.2
*/
@Configuration
@ -41,7 +43,7 @@ class GsonHttpMessageConvertersConfiguration {
@Configuration
@ConditionalOnBean(Gson.class)
@Conditional(PreferGsonOrMissingJacksonCondition.class)
@Conditional(PreferGsonOrMissingJacksonAndJsonbCondition.class)
protected static class GsonHttpMessageConverterConfiguration {
@Bean
@ -54,9 +56,9 @@ class GsonHttpMessageConvertersConfiguration {
}
private static class PreferGsonOrMissingJacksonCondition extends AnyNestedCondition {
private static class PreferGsonOrMissingJacksonAndJsonbCondition extends AnyNestedCondition {
PreferGsonOrMissingJacksonCondition() {
PreferGsonOrMissingJacksonAndJsonbCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ -65,11 +67,29 @@ class GsonHttpMessageConvertersConfiguration {
}
@ConditionalOnMissingBean(MappingJackson2HttpMessageConverter.class)
@Conditional(JacksonAndJsonbMissing.class)
static class JacksonJsonbMissing {
}
}
private static class JacksonAndJsonbMissing extends NoneNestedConditions {
JacksonAndJsonbMissing() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnBean(MappingJackson2HttpMessageConverter.class)
static class JacksonMissing {
}
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "jsonb")
static class JsonbMissing {
}
}
}

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

@ -0,0 +1,94 @@
/*
* 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.boot.autoconfigure.condition.NoneNestedConditions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
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", matchIfMissing = false)
static class JsonbPreferred {
}
@Conditional(JacksonAndGsonMissing.class)
static class JacksonGsonMissing {
}
}
private static class JacksonAndGsonMissing extends NoneNestedConditions {
JacksonAndGsonMissing() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnBean(MappingJackson2HttpMessageConverter.class)
static class JacksonMissing {
}
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "gson")
static class GsonMissing {
}
}
}

@ -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();
}
}

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

@ -1362,6 +1362,9 @@
},
{
"value": "jackson"
},
{
"value": "jsonb"
}
],
"providers": [

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

@ -19,6 +19,8 @@ package org.springframework.boot.autoconfigure.http;
import java.util.Arrays;
import java.util.List;
import javax.json.bind.Jsonb;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import org.junit.After;
@ -28,6 +30,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration;
import org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration.MappingJackson2HttpMessageConverterConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
@ -38,6 +41,7 @@ import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessage
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.GsonHttpMessageConverter;
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.xml.MappingJackson2XmlHttpMessageConverter;
@ -51,6 +55,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author David Liu
* @author Andy Wilkinson
* @author Sebastien Deleuze
* @author Eddú Meléndez
*/
public class HttpMessageConvertersAutoConfigurationTests {
@ -140,19 +145,20 @@ public class HttpMessageConvertersAutoConfigurationTests {
@Test
public void jacksonIsPreferredByDefaultWhenBothGsonAndJacksonAreAvailable() {
this.context.register(GsonAutoConfiguration.class, JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class);
JsonbAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class);
this.context.refresh();
assertConverterBeanExists(MappingJackson2HttpMessageConverter.class,
"mappingJackson2HttpMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
MappingJackson2HttpMessageConverter.class);
assertThat(this.context.getBeansOfType(GsonHttpMessageConverter.class)).isEmpty();
assertThat(this.context.getBeansOfType(JsonbHttpMessageConverter.class)).isEmpty();
}
@Test
public void gsonCanBePreferredWhenBothGsonAndJacksonAreAvailable() {
this.context.register(GsonAutoConfiguration.class, JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class);
JsonbAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class);
TestPropertyValues.of("spring.http.converters.preferred-json-mapper:gson")
.applyTo(this.context);
this.context.refresh();
@ -160,6 +166,8 @@ public class HttpMessageConvertersAutoConfigurationTests {
"gsonHttpMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
GsonHttpMessageConverter.class);
assertThat(this.context.getBeansOfType(JsonbHttpMessageConverter.class))
.isEmpty();
assertThat(this.context.getBeansOfType(MappingJackson2HttpMessageConverter.class))
.isEmpty();
}
@ -176,6 +184,55 @@ public class HttpMessageConvertersAutoConfigurationTests {
GsonHttpMessageConverter.class);
}
@Test
public void noJsonb() throws Exception {
this.context.register(HttpMessageConvertersAutoConfiguration.class);
this.context.refresh();
assertThat(this.context.getBeansOfType(Jsonb.class).isEmpty()).isTrue();
assertThat(this.context.getBeansOfType(JsonbHttpMessageConverter.class).isEmpty())
.isTrue();
}
@Test
public void defaultJsonbConverter() throws Exception {
this.context.register(JsonbAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class);
this.context.refresh();
assertConverterBeanExists(JsonbHttpMessageConverter.class,
"jsonbHttpMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
JsonbHttpMessageConverter.class);
}
@Test
public void jsonbCanBePreferredWhenBothGsonAndJacksonAreAvailable() {
this.context.register(GsonAutoConfiguration.class, JacksonAutoConfiguration.class,
JsonbAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class);
TestPropertyValues.of("spring.http.converters.preferred-json-mapper:jsonb")
.applyTo(this.context);
this.context.refresh();
assertConverterBeanExists(JsonbHttpMessageConverter.class,
"jsonbHttpMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
JsonbHttpMessageConverter.class);
assertThat(this.context.getBeansOfType(GsonHttpMessageConverter.class))
.isEmpty();
assertThat(this.context.getBeansOfType(MappingJackson2HttpMessageConverter.class))
.isEmpty();
}
@Test
public void customJsonbConverter() throws Exception {
this.context.register(JsonbAutoConfiguration.class, JsonbConverterConfig.class,
HttpMessageConvertersAutoConfiguration.class);
this.context.refresh();
assertConverterBeanExists(JsonbHttpMessageConverter.class,
"customJsonbMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
JsonbHttpMessageConverter.class);
}
@Test
public void defaultStringConverter() throws Exception {
this.context.register(HttpMessageConvertersAutoConfiguration.class);
@ -288,6 +345,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
protected static class StringConverterConfig {

@ -0,0 +1,72 @@
/*
* 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.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link JsonbAutoConfiguration}.
*
* @author Eddú Meléndez
*/
public class JsonbAutoConfigurationTests {
AnnotationConfigApplicationContext context;
@Before
public void setUp() {
this.context = new AnnotationConfigApplicationContext();
}
@After
public void tearDown() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void jsonbRegistration() {
this.context.register(JsonbAutoConfiguration.class);
this.context.refresh();
Jsonb jsonb = this.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;
}
}
}

@ -118,8 +118,11 @@
<jna.version>4.4.0</jna.version>
<joda-time.version>2.9.9</joda-time.version>
<jolokia.version>1.3.7</jolokia.version>
<johnzon-jsonb.version>1.1.2</johnzon-jsonb.version>
<jooq.version>3.9.5</jooq.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>
<jstl.version>1.2</jstl.version>
<jtds.version>1.3.1</jtds.version>
@ -938,6 +941,16 @@
<artifactId>javax.jms-api</artifactId>
<version>${javax-jms.version}</version>
</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>
<groupId>javax.mail</groupId>
<artifactId>javax.mail-api</artifactId>
@ -1305,6 +1318,11 @@
<artifactId>httpmime</artifactId>
<version>${httpclient.version}</version>
</dependency>
<dependency>
<groupId>org.apache.johnzon</groupId>
<artifactId>johnzon-jsonb</artifactId>
<version>${johnzon-jsonb.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-bom</artifactId>

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

@ -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.GsonTester;
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
@ -45,8 +46,8 @@ import org.springframework.boot.test.json.JacksonTester;
public @interface AutoConfigureJsonTesters {
/**
* If {@link BasicJsonTester}, {@link JacksonTester} and {@link GsonTester} beans
* should be registered. Defaults to {@code true}
* If {@link BasicJsonTester}, {@link JacksonTester}, {@link JsonbTester} and
* {@link GsonTester} beans should be registered. Defaults to {@code true}
* @return if tester support is enabled
*/
boolean enabled() default true;

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

@ -19,6 +19,8 @@ package org.springframework.boot.test.autoconfigure.json;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import javax.json.bind.Jsonb;
import com.fasterxml.jackson.databind.ObjectMapper;
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.gson.GsonAutoConfiguration;
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.BasicJsonTester;
import org.springframework.boot.test.json.GsonTester;
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.Configuration;
import org.springframework.context.annotation.Scope;
@ -48,13 +52,15 @@ import org.springframework.util.ReflectionUtils;
* Auto-configuration for Json testers.
*
* @author Phillip Webb
* @author Eddú Meléndez
* @see AutoConfigureJsonTesters
* @since 1.4.0
*/
@Configuration
@ConditionalOnClass(name = "org.assertj.core.api.Assert")
@ConditionalOnProperty("spring.test.jsontesters.enabled")
@AutoConfigureAfter({ JacksonAutoConfiguration.class, GsonAutoConfiguration.class })
@AutoConfigureAfter({ JacksonAutoConfiguration.class, GsonAutoConfiguration.class,
JsonbAutoConfiguration.class })
public class JsonTestersAutoConfiguration {
@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.
*/

@ -67,13 +67,15 @@ org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
# AutoConfigureJson auto-configuration imports
org.springframework.boot.test.autoconfigure.json.AutoConfigureJson=\
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
org.springframework.boot.test.autoconfigure.json.AutoConfigureJsonTesters=\
org.springframework.boot.test.autoconfigure.json.JsonTestersAutoConfiguration,\
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
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.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
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.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration.\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
@ -145,4 +149,4 @@ org.springframework.test.context.TestExecutionListener=\
org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener,\
org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener,\
org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener,\
org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener
org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener

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

@ -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.GsonTester;
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.junit4.SpringRunner;
@ -50,6 +51,9 @@ public class JsonTestWithAutoConfigureJsonTestersTests {
@Autowired(required = false)
private GsonTester<ExampleBasicObject> gsonTester;
@Autowired(required = false)
private JsonbTester<ExampleBasicObject> jsonbTester;
@Test
public void basicJson() throws Exception {
assertThat(this.basicJson).isNull();
@ -65,4 +69,9 @@ public class JsonTestWithAutoConfigureJsonTestersTests {
assertThat(this.gsonTester).isNull();
}
@Test
public void jsonb() throws Exception {
assertThat(this.jsonbTester).isNull();
}
}

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

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

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

@ -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());
}
}
Loading…
Cancel
Save