diff --git a/avaje-config-json/pom.xml b/avaje-config-json/pom.xml
new file mode 100644
index 00000000..7dafcdff
--- /dev/null
+++ b/avaje-config-json/pom.xml
@@ -0,0 +1,82 @@
+
+
+ 4.0.0
+
+ org.avaje
+ java11-oss
+ 4.4
+
+
+
+ io.avaje
+ avaje-config-json
+ 4.1-SNAPSHOT
+
+
+ scm:git:git@github.com:avaje/avaje-config.git
+ scm:git:git@github.com:avaje/avaje-config.git
+ HEAD
+
+
+
+ 2.3
+ true
+ false
+
+
+
+
+ io.avaje
+ avaje-config
+ ${project.version}
+
+
+ io.avaje
+ avaje-jsonb
+ 2.1
+
+
+
+ io.avaje
+ junit
+ 1.5
+ test
+
+
+
+ ch.qos.logback
+ logback-classic
+ 1.5.7
+ test
+
+
+
+ io.avaje
+ avaje-applog-slf4j
+ 1.0
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-repository-plugin
+ 2.4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/avaje-config-json/src/main/java/io/avaje/config/json/JsonParser.java b/avaje-config-json/src/main/java/io/avaje/config/json/JsonParser.java
new file mode 100644
index 00000000..abedba2a
--- /dev/null
+++ b/avaje-config-json/src/main/java/io/avaje/config/json/JsonParser.java
@@ -0,0 +1,41 @@
+package io.avaje.config.json;
+
+import io.avaje.config.ConfigParser;
+import io.avaje.jsonb.Jsonb;
+import io.avaje.jsonb.Types;
+import org.jspecify.annotations.NullMarked;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.util.Map;
+
+@NullMarked
+final class JsonParser implements ConfigParser {
+
+ private static final String[] extensions = {"json"};
+
+ private final Jsonb jsonb;
+
+ JsonParser() {
+ this.jsonb = Jsonb.builder()
+ .failOnUnknown(false)
+ .serializeEmpty(true)
+ .serializeNulls(true)
+ .build();
+ }
+
+ @Override
+ public String[] supportedExtensions() {
+ return extensions;
+ }
+
+ @Override
+ public Map load(Reader reader) {
+ return (Map) this.jsonb.type(Types.mapOf(String.class)).fromJson(reader);
+ }
+
+ @Override
+ public Map load(InputStream is) {
+ return (Map) this.jsonb.type(Types.mapOf(String.class)).fromJson(is);
+ }
+}
diff --git a/avaje-config-json/src/main/java/module-info.java b/avaje-config-json/src/main/java/module-info.java
new file mode 100644
index 00000000..ffa550ae
--- /dev/null
+++ b/avaje-config-json/src/main/java/module-info.java
@@ -0,0 +1,10 @@
+module io.avaje.config.json {
+
+ requires io.avaje.config;
+ requires io.avaje.jsonb;
+
+ exports io.avaje.config.json;
+
+ uses io.avaje.config.ConfigExtension;
+
+}
diff --git a/avaje-config-json/src/test/java/io/avaje/config/json/JsonParserTest.java b/avaje-config-json/src/test/java/io/avaje/config/json/JsonParserTest.java
new file mode 100644
index 00000000..e5daa940
--- /dev/null
+++ b/avaje-config-json/src/test/java/io/avaje/config/json/JsonParserTest.java
@@ -0,0 +1,51 @@
+package io.avaje.config.json;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.*;
+
+public class JsonParserTest {
+
+ @Test
+ void supportedExtensions() {
+ var parser = new JsonParser();
+ assertThat(parser.supportedExtensions()).isEqualTo(new String[]{"json"});
+ }
+
+ private static String input() {
+ return "{" +
+ "\"one.key\": \"a\"," +
+ "\"one.key2\": \"b\"," +
+ "\"key3\": \"c\"" +
+ "}";
+ }
+
+ @Test
+ void load_reader() {
+ var parser = new JsonParser();
+ Map map = parser.load(new StringReader(input()));
+
+ assertThat(map).hasSize(3);
+ assertThat(map).containsOnlyKeys("one.key", "one.key2", "key3");
+ assertThat(map).containsEntry("one.key", "a");
+ assertThat(map).containsEntry("one.key2", "b");
+ assertThat(map).containsEntry("key3", "c");
+ }
+
+ @Test
+ void load_inputStream() {
+ var parser = new JsonParser();
+ Map map = parser.load(new ByteArrayInputStream(input().getBytes(StandardCharsets.UTF_8)));
+
+ assertThat(map).hasSize(3);
+ assertThat(map).containsOnlyKeys("one.key", "one.key2", "key3");
+ assertThat(map).containsEntry("one.key", "a");
+ assertThat(map).containsEntry("one.key2", "b");
+ assertThat(map).containsEntry("key3", "c");
+ }
+}
diff --git a/pom.xml b/pom.xml
index 51cc3c02..ec538067 100644
--- a/pom.xml
+++ b/pom.xml
@@ -15,6 +15,7 @@
avaje-config
+ avaje-config-json
avaje-aws-appconfig
avaje-dynamic-logback