com.fasterxml.jackson.core
jackson-databind
@@ -155,6 +162,25 @@
+
+ org.moditect
+ moditect-maven-plugin
+
+
+ add-module-infos
+ package
+
+ add-module-info
+
+
+ true
+
+ module-info.java
+
+
+
+
+
diff --git a/http-client/src/main/java/io/avaje/http/client/DHttpClientBuilder.java b/http-client/src/main/java/io/avaje/http/client/DHttpClientBuilder.java
index 1dd2b2f80..8bb2f3cda 100644
--- a/http-client/src/main/java/io/avaje/http/client/DHttpClientBuilder.java
+++ b/http-client/src/main/java/io/avaje/http/client/DHttpClientBuilder.java
@@ -138,6 +138,9 @@ private BodyAdapter defaultBodyAdapter() {
if (bootLayer.findModule("com.fasterxml.jackson.databind").isPresent()) {
return new JacksonBodyAdapter();
}
+ if (bootLayer.findModule("tools.jackson.databind").isPresent()) {
+ return new Jackson3BodyAdapter();
+ }
return bodyAdapter;
})
.orElseGet(() -> {
@@ -148,6 +151,11 @@ private BodyAdapter defaultBodyAdapter() {
}
try {
return new JacksonBodyAdapter();
+ } catch (Throwable e) {
+ // I guess it don't exist
+ }
+ try {
+ return new Jackson3BodyAdapter();
} catch (Throwable e) {
return bodyAdapter;
}
diff --git a/http-client/src/main/java/io/avaje/http/client/Jackson3BodyAdapter.java b/http-client/src/main/java/io/avaje/http/client/Jackson3BodyAdapter.java
new file mode 100644
index 000000000..03cea6e62
--- /dev/null
+++ b/http-client/src/main/java/io/avaje/http/client/Jackson3BodyAdapter.java
@@ -0,0 +1,177 @@
+package io.avaje.http.client;
+
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+import tools.jackson.databind.ObjectMapper;
+import tools.jackson.databind.ObjectReader;
+import tools.jackson.databind.ObjectWriter;
+import tools.jackson.databind.type.CollectionType;
+
+/**
+ * Jackson 3.x BodyAdapter to read and write beans as JSON.
+ *
+ * {@code
+ * HttpClient.builder()
+ * .baseUrl(baseUrl)
+ * .bodyAdapter(new Jackson3BodyAdapter())
+ * .build();
+ *
+ * }
+ */
+public final class Jackson3BodyAdapter implements BodyAdapter {
+
+ private final ObjectMapper mapper;
+
+ private final ConcurrentHashMap> beanWriterCache = new ConcurrentHashMap<>();
+ private final ConcurrentHashMap> beanReaderCache = new ConcurrentHashMap<>();
+ private final ConcurrentHashMap> listReaderCache = new ConcurrentHashMap<>();
+
+ /** Create passing the ObjectMapper to use. */
+ public Jackson3BodyAdapter(ObjectMapper mapper) {
+ this.mapper = mapper;
+ }
+
+ /** Create with a ObjectMapper that allows unknown properties and inclusion non empty. */
+ public Jackson3BodyAdapter() {
+ this.mapper = new ObjectMapper();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public BodyWriter beanWriter(Class> cls) {
+ return (BodyWriter)
+ beanWriterCache.computeIfAbsent(
+ cls,
+ aClass -> {
+ try {
+ return new JWriter<>(mapper.writerFor(cls));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public BodyReader beanReader(Class cls) {
+ return (BodyReader)
+ beanReaderCache.computeIfAbsent(
+ cls,
+ aClass -> {
+ try {
+ return new JReader<>(mapper.readerFor(cls));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public BodyWriter beanWriter(Type cls) {
+ return (BodyWriter)
+ beanWriterCache.computeIfAbsent(
+ cls,
+ aClass -> {
+ try {
+ return new JWriter<>(mapper.writerFor(mapper.getTypeFactory().constructType(cls)));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public BodyReader beanReader(Type cls) {
+ return (BodyReader)
+ beanReaderCache.computeIfAbsent(
+ cls,
+ aClass -> {
+ try {
+ return new JReader<>(mapper.readerFor(mapper.getTypeFactory().constructType(cls)));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public BodyReader> listReader(Class cls) {
+ return (BodyReader>)
+ listReaderCache.computeIfAbsent(
+ cls,
+ aClass -> {
+ try {
+ final CollectionType collectionType =
+ mapper.getTypeFactory().constructCollectionType(List.class, cls);
+ final ObjectReader reader = mapper.readerFor(collectionType);
+ return new JReader<>(reader);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public BodyReader> listReader(Type type) {
+ return (BodyReader>)
+ listReaderCache.computeIfAbsent(
+ type,
+ aType -> {
+ try {
+ var javaType = mapper.getTypeFactory().constructType(aType);
+ final CollectionType collectionType =
+ mapper.getTypeFactory().constructCollectionType(List.class, javaType);
+ final ObjectReader reader = mapper.readerFor(collectionType);
+ return new JReader<>(reader);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ private static final class JReader implements BodyReader {
+
+ private final ObjectReader reader;
+
+ JReader(ObjectReader reader) {
+ this.reader = reader;
+ }
+
+ @Override
+ public T readBody(String content) {
+ return reader.readValue(content);
+ }
+
+ @Override
+ public T read(BodyContent bodyContent) {
+ return reader.readValue(bodyContent.content());
+ }
+ }
+
+ private static final class JWriter implements BodyWriter {
+
+ private final ObjectWriter writer;
+
+ public JWriter(ObjectWriter writer) {
+ this.writer = writer;
+ }
+
+ @Override
+ public BodyContent write(T bean, String contentType) {
+ // ignoring the requested contentType and always
+ // writing the body as json content
+ return write(bean);
+ }
+
+ @Override
+ public BodyContent write(T bean) {
+ return BodyContent.asJson(writer.writeValueAsBytes(bean));
+ }
+ }
+}
From 40aae1204a784e87ed488c670f16ba6ae02ab890 Mon Sep 17 00:00:00 2001
From: Rob Bygrave
Date: Tue, 16 Dec 2025 22:38:34 +1300
Subject: [PATCH 2/2] Format and remove extra public modifier
---
.../http/client/Jackson3BodyAdapter.java | 118 ++++++++----------
1 file changed, 50 insertions(+), 68 deletions(-)
diff --git a/http-client/src/main/java/io/avaje/http/client/Jackson3BodyAdapter.java b/http-client/src/main/java/io/avaje/http/client/Jackson3BodyAdapter.java
index 03cea6e62..20557cadc 100644
--- a/http-client/src/main/java/io/avaje/http/client/Jackson3BodyAdapter.java
+++ b/http-client/src/main/java/io/avaje/http/client/Jackson3BodyAdapter.java
@@ -41,98 +41,80 @@ public Jackson3BodyAdapter() {
@SuppressWarnings("unchecked")
@Override
public BodyWriter beanWriter(Class> cls) {
- return (BodyWriter)
- beanWriterCache.computeIfAbsent(
- cls,
- aClass -> {
- try {
- return new JWriter<>(mapper.writerFor(cls));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ return (BodyWriter) beanWriterCache.computeIfAbsent(cls, aClass -> {
+ try {
+ return new JWriter<>(mapper.writerFor(cls));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
}
@SuppressWarnings("unchecked")
@Override
public BodyReader beanReader(Class cls) {
- return (BodyReader)
- beanReaderCache.computeIfAbsent(
- cls,
- aClass -> {
- try {
- return new JReader<>(mapper.readerFor(cls));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ return (BodyReader) beanReaderCache.computeIfAbsent(cls, aClass -> {
+ try {
+ return new JReader<>(mapper.readerFor(cls));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
}
@SuppressWarnings("unchecked")
@Override
public BodyWriter beanWriter(Type cls) {
- return (BodyWriter)
- beanWriterCache.computeIfAbsent(
- cls,
- aClass -> {
- try {
- return new JWriter<>(mapper.writerFor(mapper.getTypeFactory().constructType(cls)));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ return (BodyWriter) beanWriterCache.computeIfAbsent(cls, aClass -> {
+ try {
+ return new JWriter<>(mapper.writerFor(mapper.getTypeFactory().constructType(cls)));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
}
@SuppressWarnings("unchecked")
@Override
public BodyReader beanReader(Type cls) {
- return (BodyReader)
- beanReaderCache.computeIfAbsent(
- cls,
- aClass -> {
- try {
- return new JReader<>(mapper.readerFor(mapper.getTypeFactory().constructType(cls)));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ return (BodyReader) beanReaderCache.computeIfAbsent(cls, aClass -> {
+ try {
+ return new JReader<>(mapper.readerFor(mapper.getTypeFactory().constructType(cls)));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
}
@SuppressWarnings("unchecked")
@Override
public BodyReader> listReader(Class cls) {
- return (BodyReader>)
- listReaderCache.computeIfAbsent(
- cls,
- aClass -> {
- try {
- final CollectionType collectionType =
- mapper.getTypeFactory().constructCollectionType(List.class, cls);
- final ObjectReader reader = mapper.readerFor(collectionType);
- return new JReader<>(reader);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ return (BodyReader>) listReaderCache.computeIfAbsent(cls, aClass -> {
+ try {
+ final CollectionType collectionType =
+ mapper.getTypeFactory().constructCollectionType(List.class, cls);
+ final ObjectReader reader = mapper.readerFor(collectionType);
+ return new JReader<>(reader);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
}
@SuppressWarnings("unchecked")
@Override
public BodyReader> listReader(Type type) {
- return (BodyReader>)
- listReaderCache.computeIfAbsent(
- type,
- aType -> {
- try {
- var javaType = mapper.getTypeFactory().constructType(aType);
- final CollectionType collectionType =
- mapper.getTypeFactory().constructCollectionType(List.class, javaType);
- final ObjectReader reader = mapper.readerFor(collectionType);
- return new JReader<>(reader);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ return (BodyReader>) listReaderCache.computeIfAbsent(type, aType -> {
+ try {
+ var javaType = mapper.getTypeFactory().constructType(aType);
+ final CollectionType collectionType =
+ mapper.getTypeFactory().constructCollectionType(List.class, javaType);
+ final ObjectReader reader = mapper.readerFor(collectionType);
+ return new JReader<>(reader);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
}
private static final class JReader implements BodyReader {
@@ -158,7 +140,7 @@ private static final class JWriter implements BodyWriter {
private final ObjectWriter writer;
- public JWriter(ObjectWriter writer) {
+ JWriter(ObjectWriter writer) {
this.writer = writer;
}