diff --git a/Changelog.md b/Changelog.md
new file mode 100644
index 00000000..266e84bb
--- /dev/null
+++ b/Changelog.md
@@ -0,0 +1,5 @@
+# Changelog
+
+## 2.2.0
+- [#451](https://github.com/wultra/java-core/issues/451) - Migrated to Spring Boot 4 and Jackson 3.
+
diff --git a/audit-base/pom.xml b/audit-base/pom.xml
index ecdbe526..9ca8fcc5 100644
--- a/audit-base/pom.xml
+++ b/audit-base/pom.xml
@@ -20,13 +20,9 @@
spring-boot-starter-jdbc
- com.fasterxml.jackson.core
+ tools.jackson.core
jackson-databind
-
- com.fasterxml.jackson.datatype
- jackson-datatype-jsr310
-
jakarta.annotation
jakarta.annotation-api
diff --git a/audit-base/src/main/java/com/wultra/core/audit/base/util/JsonUtil.java b/audit-base/src/main/java/com/wultra/core/audit/base/util/JsonUtil.java
index fede336f..e805f66d 100644
--- a/audit-base/src/main/java/com/wultra/core/audit/base/util/JsonUtil.java
+++ b/audit-base/src/main/java/com/wultra/core/audit/base/util/JsonUtil.java
@@ -16,12 +16,11 @@
package com.wultra.core.audit.base.util;
import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializationFeature;
-import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import tools.jackson.core.JacksonException;
+import tools.jackson.databind.ObjectMapper;
+import tools.jackson.databind.json.JsonMapper;
import java.util.Map;
@@ -34,14 +33,10 @@ public class JsonUtil {
private static final Logger logger = LoggerFactory.getLogger(JsonUtil.class);
- private final ObjectMapper objectMapper = new ObjectMapper();
-
- {
- objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
- objectMapper.registerModule(new JavaTimeModule());
- objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
- objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
- }
+ private final ObjectMapper objectMapper = JsonMapper.builder()
+ .changeDefaultPropertyInclusion(incl -> incl
+ .withValueInclusion(JsonInclude.Include.NON_EMPTY))
+ .build();
/**
* Serialize an object into JSON.
@@ -51,7 +46,7 @@ public class JsonUtil {
public String serializeObject(Object o) {
try {
return objectMapper.writeValueAsString(o);
- } catch (JsonProcessingException ex) {
+ } catch (JacksonException ex) {
logger.warn(ex.getMessage(), ex);
}
return "";
@@ -65,7 +60,7 @@ public String serializeObject(Object o) {
public String serializeMap(Map map) {
try {
return objectMapper.writeValueAsString(map);
- } catch (JsonProcessingException ex) {
+ } catch (JacksonException ex) {
logger.warn(ex.getMessage(), ex);
}
return "{}";
diff --git a/audit-base/src/test/java/com/wultra/core/audit/base/database/DatabaseAuditWriterTest.java b/audit-base/src/test/java/com/wultra/core/audit/base/database/DatabaseAuditWriterTest.java
index 36002af7..72a6f097 100644
--- a/audit-base/src/test/java/com/wultra/core/audit/base/database/DatabaseAuditWriterTest.java
+++ b/audit-base/src/test/java/com/wultra/core/audit/base/database/DatabaseAuditWriterTest.java
@@ -66,7 +66,7 @@ void testAuditScheduledCleanup() {
assertEquals(1, countAuditLogs(jdbcTemplate));
Awaitility.await()
- .atMost(Duration.ofSeconds(5))
+ .atMost(Duration.ofSeconds(6))
.until(() -> countAuditLogs(jdbcTemplate) == 0);
}
}
diff --git a/http-common/pom.xml b/http-common/pom.xml
index 1ca8675c..6485eae6 100644
--- a/http-common/pom.xml
+++ b/http-common/pom.xml
@@ -31,7 +31,7 @@
test
- com.fasterxml.jackson.core
+ tools.jackson.core
jackson-databind
test
diff --git a/http-common/src/test/java/com/wultra/core/http/common/headers/UserAgentTest.java b/http-common/src/test/java/com/wultra/core/http/common/headers/UserAgentTest.java
index 37962542..ee57d1b5 100644
--- a/http-common/src/test/java/com/wultra/core/http/common/headers/UserAgentTest.java
+++ b/http-common/src/test/java/com/wultra/core/http/common/headers/UserAgentTest.java
@@ -15,11 +15,12 @@
*/
package com.wultra.core.http.common.headers;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
+import tools.jackson.core.JacksonException;
+import tools.jackson.databind.ObjectMapper;
+import tools.jackson.databind.json.JsonMapper;
import java.util.stream.Stream;
@@ -44,7 +45,7 @@ void testParse(final String userAgent, final UserAgent.Device expectedDevice) {
assertEquals(expectedDevice, deviceOptional.get());
}
- private static Stream provideUserAgents() throws JsonProcessingException {
+ private static Stream provideUserAgents() throws JacksonException {
return Stream.of(
Arguments.of("PowerAuthNetworking/1.1.7 (en; cellular) com.wultra.app.Mobile-Token.wultra_test/2.0.0 (Apple; iOS/16.6.1; iphone12,3)", readDevice("""
{
@@ -127,8 +128,9 @@ private static Stream provideUserAgents() throws JsonProcessingExcept
);
}
- private static UserAgent.Device readDevice(final String json) throws JsonProcessingException {
- return new ObjectMapper().readValue(json, UserAgent.Device.class);
+ private static UserAgent.Device readDevice(final String json) throws JacksonException {
+ ObjectMapper objectMapper = JsonMapper.builder().build();
+ return objectMapper.readValue(json, UserAgent.Device.class);
}
}
diff --git a/pom.xml b/pom.xml
index 4236b05a..919853e5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,7 +62,7 @@
3.6.2
- 3.5.14
+ 4.0.6
7.7.0
diff --git a/rest-client-base/pom.xml b/rest-client-base/pom.xml
index 73140760..0f727779 100644
--- a/rest-client-base/pom.xml
+++ b/rest-client-base/pom.xml
@@ -43,12 +43,19 @@
com.wultra.core
rest-model-base
${project.version}
- compile
org.slf4j
slf4j-api
+
+ tools.jackson.core
+ jackson-databind
+
+
+ io.netty
+ netty-transport-classes-epoll
+
diff --git a/rest-client-base/src/main/java/com/wultra/core/rest/client/base/DefaultRestClient.java b/rest-client-base/src/main/java/com/wultra/core/rest/client/base/DefaultRestClient.java
index 7bccdb7e..1e3aaca0 100644
--- a/rest-client-base/src/main/java/com/wultra/core/rest/client/base/DefaultRestClient.java
+++ b/rest-client-base/src/main/java/com/wultra/core/rest/client/base/DefaultRestClient.java
@@ -15,9 +15,6 @@
*/
package com.wultra.core.rest.client.base;
-import com.fasterxml.jackson.databind.Module;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.type.TypeFactory;
import com.wultra.core.rest.client.base.util.SslUtils;
import com.wultra.core.rest.model.base.request.ObjectRequest;
import com.wultra.core.rest.model.base.response.ErrorResponse;
@@ -29,17 +26,17 @@
import io.netty.handler.logging.LogLevel;
import io.netty.handler.ssl.SslContext;
import jdk.net.ExtendedSocketOptions;
+import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferLimitException;
import org.springframework.http.*;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.http.codec.ClientCodecConfigurer;
-import org.springframework.http.codec.json.Jackson2JsonDecoder;
-import org.springframework.http.codec.json.Jackson2JsonEncoder;
+import org.springframework.http.codec.json.JacksonJsonDecoder;
+import org.springframework.http.codec.json.JacksonJsonEncoder;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
@@ -50,8 +47,12 @@
import reactor.netty.tcp.SslProvider;
import reactor.netty.transport.ProxyProvider;
import reactor.netty.transport.logging.AdvancedByteBufFormat;
+import tools.jackson.databind.DatabindException;
+import tools.jackson.databind.JacksonModule;
+import tools.jackson.databind.ObjectMapper;
+import tools.jackson.databind.json.JsonMapper;
+import tools.jackson.databind.type.TypeFactory;
-import java.io.IOException;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.URISyntaxException;
@@ -65,10 +66,9 @@
*
* @author Roman Strobl, roman.strobl@wultra.com
*/
+@Slf4j
public class DefaultRestClient implements RestClient {
- private static final Logger logger = LoggerFactory.getLogger(DefaultRestClient.class);
-
/**
* Default max connections.
* As same value as in {@link reactor.netty.tcp.TcpResources#get()} avoid default to {@code 2 * available number of processors} only.
@@ -77,7 +77,7 @@ public class DefaultRestClient implements RestClient {
private WebClient webClient;
private final RestClientConfiguration config;
- private final Collection modules;
+ private final Collection modules;
/**
* Construct default REST client without any additional configuration.
@@ -98,7 +98,7 @@ public DefaultRestClient(String baseUrl) throws RestClientException {
* @param modules jackson modules
* @throws RestClientException Thrown in case client initialization fails.
*/
- public DefaultRestClient(final RestClientConfiguration config, final Module... modules) throws RestClientException {
+ public DefaultRestClient(final RestClientConfiguration config, final JacksonModule... modules) throws RestClientException {
// Use WebClient configuration from the config constructor parameter
this.config = config;
this.modules = modules == null ? Collections.emptyList() : Arrays.asList(modules);
@@ -172,13 +172,13 @@ private void initializeWebClient() throws RestClientException {
});
}
- final Optional objectMapperOptional = createObjectMapper(config, modules);
+ final Optional objectMapperOptional = createObjectMapper(config, modules);
final ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
.codecs(configurer -> {
ClientCodecConfigurer.ClientDefaultCodecs defaultCodecs = configurer.defaultCodecs();
- objectMapperOptional.ifPresent(objectMapper -> {
- defaultCodecs.jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper, MediaType.APPLICATION_JSON));
- defaultCodecs.jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper, MediaType.APPLICATION_JSON));
+ objectMapperOptional.ifPresent(mapper -> {
+ defaultCodecs.jacksonJsonEncoder(new JacksonJsonEncoder(mapper, MediaType.APPLICATION_JSON));
+ defaultCodecs.jacksonJsonDecoder(new JacksonJsonDecoder(mapper, MediaType.APPLICATION_JSON));
});
defaultCodecs.maxInMemorySize(config.getMaxInMemorySize());
})
@@ -270,21 +270,20 @@ private static HttpClient createHttpClient(final RestClientConfiguration config)
return HttpClient.create(providerBuilder.build());
}
- private static Optional createObjectMapper(final RestClientConfiguration config, Collection modules) {
+ private static Optional createObjectMapper(final RestClientConfiguration config, Collection modules) {
final RestClientConfiguration.JacksonConfiguration jacksonConfiguration = config.getJacksonConfiguration();
if (jacksonConfiguration == null && modules.isEmpty()) {
return Optional.empty();
}
logger.debug("Configuring object mapper");
- final ObjectMapper objectMapper = new ObjectMapper();
+ JsonMapper.Builder builder = JsonMapper.builder();
+ builder.addModules(modules);
if (jacksonConfiguration != null) {
- jacksonConfiguration.getDeserialization().forEach(objectMapper::configure);
- jacksonConfiguration.getSerialization().forEach(objectMapper::configure);
+ jacksonConfiguration.getDeserialization().forEach(builder::configure);
+ jacksonConfiguration.getSerialization().forEach(builder::configure);
}
- objectMapper.registerModules(modules);
-
- return Optional.of(objectMapper);
+ return Optional.of(builder.build());
}
private static void validateConfiguration(final RestClientConfiguration config) throws RestClientException {
@@ -304,7 +303,7 @@ public ResponseEntity get(String path, MultiValueMap quer
return buildUri(webClient.get(), path, queryParams)
.headers(h -> {
if (headers != null) {
- h.addAll(headers);
+ headers.forEach(h::addAll);
}
})
.exchangeToMono(rs -> handleResponse(rs, responseType))
@@ -329,7 +328,7 @@ public void getNonBlocking(String path, MultiValueMap queryP
buildUri(webClient.get(), path, queryParams)
.headers(h -> {
if (headers != null) {
- h.addAll(headers);
+ headers.forEach(h::addAll);
}
})
.accept(config.getAcceptType())
@@ -373,7 +372,7 @@ public ResponseEntity post(String path, Object request, MultiValueMap {
if (headers != null) {
- h.addAll(headers);
+ headers.forEach(h::addAll);
}
})
.contentType(resolveContentType(config, headers))
@@ -408,7 +407,7 @@ public void postNonBlocking(String path, Object request, MultiValueMap {
if (headers != null) {
- h.addAll(headers);
+ headers.forEach(h::addAll);
}
})
.contentType(resolveContentType(config, headers))
@@ -454,7 +453,7 @@ public ResponseEntity put(String path, Object request, MultiValueMap {
if (headers != null) {
- h.addAll(headers);
+ headers.forEach(h::addAll);
}
})
.contentType(resolveContentType(config, headers))
@@ -482,7 +481,7 @@ public void putNonBlocking(String path, Object request, MultiValueMap {
if (headers != null) {
- h.addAll(headers);
+ headers.forEach(h::addAll);
}
})
.contentType(resolveContentType(config, headers))
@@ -528,7 +527,7 @@ public ResponseEntity delete(String path, MultiValueMap q
return buildUri(webClient.delete(), path, queryParams)
.headers(h -> {
if (headers != null) {
- h.addAll(headers);
+ headers.forEach(h::addAll);
}
})
.exchangeToMono(rs -> handleResponse(rs, responseType))
@@ -553,7 +552,7 @@ public void deleteNonBlocking(String path, MultiValueMap que
buildUri(webClient.delete(), path, queryParams)
.headers(h -> {
if (headers != null) {
- h.addAll(headers);
+ headers.forEach(h::addAll);
}
})
.accept(config.getAcceptType())
@@ -596,7 +595,7 @@ public ResponseEntity patch(String path, Object request, MultiValueMap {
if (headers != null) {
- h.addAll(headers);
+ headers.forEach(h::addAll);
}
})
.contentType(resolveContentType(config, headers))
@@ -624,7 +623,7 @@ public void patchNonBlocking(String path, Object request, MultiValueMap {
if (headers != null) {
- h.addAll(headers);
+ headers.forEach(h::addAll);
}
})
.contentType(resolveContentType(config, headers))
@@ -670,7 +669,7 @@ public ResponseEntity head(String path, MultiValueMap que
return buildUri(webClient.head(), path, queryParams)
.headers(h -> {
if (headers != null) {
- h.addAll(headers);
+ headers.forEach(h::addAll);
}
})
.exchangeToMono(rs -> handleResponse(rs, responseType))
@@ -696,7 +695,7 @@ public void headNonBlocking(String path, MultiValueMap query
buildUri(webClient.head(), path, queryParams)
.headers(h -> {
if (headers != null) {
- h.addAll(headers);
+ headers.forEach(h::addAll);
}
})
.accept(config.getAcceptType())
@@ -738,12 +737,8 @@ public ObjectResponse headObject(String path, MultiValueMap ParameterizedTypeReference> getTypeReference(Class responseType) {
- return new ParameterizedTypeReference<>() {
- @Override
- public Type getType() {
- return TypeFactory.defaultInstance().constructParametricType(ObjectResponse.class, responseType);
- }
- };
+ final Type type = ResolvableType.forClassWithGenerics(ObjectResponse.class, responseType).getType();
+ return ParameterizedTypeReference.forType(type);
}
/**
@@ -770,13 +765,13 @@ private Mono> handleResponse(ClientResponse response, Para
if (clazz.isAssignableFrom(ObjectResponse.class)) {
try {
// Use an ObjectMapper to deserialize the error response
- ObjectMapper objectMapper = new ObjectMapper();
+ ObjectMapper objectMapper = JsonMapper.builder().build();
ErrorResponse errorResponse = objectMapper.readValue(rawResponse, ErrorResponse.class);
if (errorResponse != null) {
return Mono.error(new RestClientException("HTTP error occurred: " + response.statusCode(), response.statusCode(), rawResponse, rawResponseHeaders, errorResponse));
}
- } catch (IOException ex) {
- // Exception is handled silently, ErrorResponse is not available, use a regular error with raw response
+ } catch (DatabindException e) {
+ logger.warn("Error occurred when parsing response body", e);
}
}
return Mono.error(new RestClientException("HTTP error occurred: " + response.statusCode(), response.statusCode(), rawResponse, rawResponseHeaders));
@@ -875,7 +870,7 @@ public static class Builder {
private final RestClientConfiguration config;
- private final Collection modules;
+ private final Collection modules;
/**
* Construct new builder with given base URL.
@@ -1096,8 +1091,8 @@ public Builder jacksonConfiguration(RestClientConfiguration.JacksonConfiguration
* @param modules Jackson modules.
* @return Builder.
*/
- public Builder modules(Collection modules) {
- modules.addAll(modules);
+ public Builder modules(Collection modules) {
+ this.modules.addAll(modules);
return this;
}
diff --git a/rest-client-base/src/main/java/com/wultra/core/rest/client/base/RestClientConfiguration.java b/rest-client-base/src/main/java/com/wultra/core/rest/client/base/RestClientConfiguration.java
index 8e941d07..fe49a229 100644
--- a/rest-client-base/src/main/java/com/wultra/core/rest/client/base/RestClientConfiguration.java
+++ b/rest-client-base/src/main/java/com/wultra/core/rest/client/base/RestClientConfiguration.java
@@ -15,14 +15,14 @@
*/
package com.wultra.core.rest.client.base;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.Getter;
import lombok.Setter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
+import tools.jackson.databind.DeserializationFeature;
+import tools.jackson.databind.SerializationFeature;
import java.time.Duration;
import java.util.Arrays;
diff --git a/rest-client-base/src/test/java/com/wultra/core/rest/client/base/DefaultRestClientTest.java b/rest-client-base/src/test/java/com/wultra/core/rest/client/base/DefaultRestClientTest.java
index b1035f3d..5ab853a4 100644
--- a/rest-client-base/src/test/java/com/wultra/core/rest/client/base/DefaultRestClientTest.java
+++ b/rest-client-base/src/test/java/com/wultra/core/rest/client/base/DefaultRestClientTest.java
@@ -18,8 +18,6 @@
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
import com.wultra.core.rest.client.base.model.TestRequest;
import com.wultra.core.rest.client.base.model.TestResponse;
import com.wultra.core.rest.model.base.request.ObjectRequest;
@@ -46,6 +44,8 @@
import org.springframework.util.MultiValueMap;
import org.springframework.util.ResourceUtils;
import reactor.core.publisher.Flux;
+import tools.jackson.databind.ObjectMapper;
+import tools.jackson.databind.json.JsonMapper;
import java.io.File;
import java.io.FileInputStream;
@@ -683,10 +683,10 @@ void testDeleteWithFullUrl() throws RestClientException {
}
@Test
- void testPostWithDataBuffer() throws RestClientException, JsonProcessingException {
+ void testPostWithDataBuffer() throws Exception {
String requestData = String.valueOf(System.currentTimeMillis());
ObjectRequest request = new ObjectRequest<>(new TestRequest(requestData));
- ObjectMapper objectMapper = new ObjectMapper();
+ ObjectMapper objectMapper = JsonMapper.builder().build();
byte[] data = objectMapper.writeValueAsBytes(request);
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
DefaultDataBuffer dataBuffer = factory.wrap(ByteBuffer.wrap(data));
@@ -706,8 +706,8 @@ void testPostWithMultipartData() throws RestClientException {
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder();
bodyBuilder.part("request", testRequest);
- HttpHeaders headers = new HttpHeaders();
- headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+ final MultiValueMap headers = new LinkedMultiValueMap<>();
+ headers.add(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE);
final ResponseEntity> responseEntity =
restClient.post("/multipart-request-response", bodyBuilder.build(), null, headers, new ParameterizedTypeReference<>(){});
@@ -720,8 +720,8 @@ void testPostWithMultipartData() throws RestClientException {
@Test
void testPostFormData() throws Exception {
- final HttpHeaders headers = new HttpHeaders();
- headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+ final MultiValueMap headers = new LinkedMultiValueMap<>();
+ headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
final MultiValueMap map = new LinkedMultiValueMap<>();
map.add("grant_type", "authorization_code");
@@ -742,8 +742,8 @@ void testPostFormData() throws Exception {
void testPostOctetStream() throws Exception {
final byte[] request = {1, 2};
- final HttpHeaders headers = new HttpHeaders();
- headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
+ final MultiValueMap headers = new LinkedMultiValueMap<>();
+ headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
final ResponseEntity> responseEntity =
restClient.post("/octet-stream", request, null, headers, new ParameterizedTypeReference<>(){});
@@ -788,7 +788,7 @@ void testDefaultHttpHeaders() throws RestClientException {
final ResponseEntity> responseEntity =
restClient.post("/request-headers-response", null, new ParameterizedTypeReference<>(){});
- assertTrue(responseEntity.getHeaders().containsKey(headerName));
+ assertTrue(responseEntity.getHeaders().containsHeader(headerName));
assertEquals(headerVaue, responseEntity.getHeaders().getFirst(headerName));
}
diff --git a/rest-model-base/pom.xml b/rest-model-base/pom.xml
index 21467c23..e925015e 100644
--- a/rest-model-base/pom.xml
+++ b/rest-model-base/pom.xml
@@ -18,7 +18,10 @@
jakarta.validation
jakarta.validation-api
- compile
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
diff --git a/rest-model-base/src/main/java/com/wultra/core/rest/model/base/response/ObjectResponse.java b/rest-model-base/src/main/java/com/wultra/core/rest/model/base/response/ObjectResponse.java
index 1be9af64..c4b62b98 100644
--- a/rest-model-base/src/main/java/com/wultra/core/rest/model/base/response/ObjectResponse.java
+++ b/rest-model-base/src/main/java/com/wultra/core/rest/model/base/response/ObjectResponse.java
@@ -16,6 +16,7 @@
package com.wultra.core.rest.model.base.response;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.EqualsAndHashCode;
@@ -30,6 +31,7 @@
*/
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
+@JsonPropertyOrder({"status", "responseObject"})
public class ObjectResponse extends Response {
@Valid