From 09a839c0fb713ecd34c3cf3d5e111da30b1c4874 Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Wed, 19 Nov 2025 12:43:30 -0500
Subject: [PATCH] nullsafe json node
---
json-node/pom.xml | 5 +-
.../json/node/adapter/DJsonNodeMapper.java | 12 +--
.../json/node/adapter/NullJsonObjectTest.java | 28 +++++++
.../adapter/WrapJsonObjectJsonAdapter.java | 83 +++++++++++++++++++
4 files changed, 119 insertions(+), 9 deletions(-)
create mode 100644 json-node/src/test/java/io/avaje/json/node/adapter/NullJsonObjectTest.java
create mode 100644 json-node/src/test/java/io/avaje/json/node/adapter/WrapJsonObjectJsonAdapter.java
diff --git a/json-node/pom.xml b/json-node/pom.xml
index 10084037..03828af3 100644
--- a/json-node/pom.xml
+++ b/json-node/pom.xml
@@ -32,13 +32,13 @@
io.avaje
avaje-json-core
- 3.9-SNAPSHOT
+ ${project.version}
io.avaje
avaje-jsonb
- 3.0-RC1
+ ${project.version}
true
@@ -48,7 +48,6 @@
1.6
test
-
diff --git a/json-node/src/main/java/io/avaje/json/node/adapter/DJsonNodeMapper.java b/json-node/src/main/java/io/avaje/json/node/adapter/DJsonNodeMapper.java
index c1fd61a4..ffc2cca8 100644
--- a/json-node/src/main/java/io/avaje/json/node/adapter/DJsonNodeMapper.java
+++ b/json-node/src/main/java/io/avaje/json/node/adapter/DJsonNodeMapper.java
@@ -22,15 +22,15 @@ final class DJsonNodeMapper implements JsonNodeMapper {
static final NumberAdapter NUMBER_ADAPTER = new NumberAdapter();
private final JsonStream jsonStream;
- private final NodeAdapter nodeAdapter;
- private final ObjectAdapter objectAdapter;
- private final ArrayAdapter arrayAdapter;
+ private final JsonAdapter nodeAdapter;
+ private final JsonAdapter objectAdapter;
+ private final JsonAdapter arrayAdapter;
DJsonNodeMapper(JsonStream jsonStream, NodeAdapter nodeAdapter, ObjectAdapter objectAdapter, ArrayAdapter arrayAdapter) {
this.jsonStream = jsonStream;
- this.nodeAdapter = nodeAdapter;
- this.objectAdapter = objectAdapter;
- this.arrayAdapter = arrayAdapter;
+ this.nodeAdapter = nodeAdapter.nullSafe();
+ this.objectAdapter = objectAdapter.nullSafe();
+ this.arrayAdapter = arrayAdapter.nullSafe();
}
@Override
diff --git a/json-node/src/test/java/io/avaje/json/node/adapter/NullJsonObjectTest.java b/json-node/src/test/java/io/avaje/json/node/adapter/NullJsonObjectTest.java
new file mode 100644
index 00000000..f41b9989
--- /dev/null
+++ b/json-node/src/test/java/io/avaje/json/node/adapter/NullJsonObjectTest.java
@@ -0,0 +1,28 @@
+package io.avaje.json.node.adapter;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.junit.jupiter.api.Test;
+
+import io.avaje.json.node.JsonObject;
+import io.avaje.jsonb.Jsonb;
+
+class NullJsonObjectTest {
+ static class WrapJsonObject {
+ public JsonObject json;
+ }
+
+ @Test
+ void test() {
+ var b = Jsonb.builder();
+ new JsonNodeComponent().register(b);
+ var type =
+ b.add(WrapJsonObject.class, WrapJsonObjectJsonAdapter::new)
+ .build()
+ .type(WrapJsonObject.class);
+ WrapJsonObject fromJson = type.fromJson("{\"json\":null}");
+ assertNull(fromJson.json);
+ assertEquals("{}", type.toJson(fromJson));
+ }
+}
diff --git a/json-node/src/test/java/io/avaje/json/node/adapter/WrapJsonObjectJsonAdapter.java b/json-node/src/test/java/io/avaje/json/node/adapter/WrapJsonObjectJsonAdapter.java
new file mode 100644
index 00000000..d9f96e1e
--- /dev/null
+++ b/json-node/src/test/java/io/avaje/json/node/adapter/WrapJsonObjectJsonAdapter.java
@@ -0,0 +1,83 @@
+package io.avaje.json.node.adapter;
+
+import java.lang.invoke.MethodHandle;
+
+import io.avaje.json.JsonAdapter;
+import io.avaje.json.JsonReader;
+import io.avaje.json.JsonWriter;
+import io.avaje.json.PropertyNames;
+import io.avaje.json.node.JsonObject;
+import io.avaje.json.node.adapter.NullJsonObjectTest.WrapJsonObject;
+import io.avaje.json.view.ViewBuilder;
+import io.avaje.json.view.ViewBuilderAware;
+import io.avaje.jsonb.Jsonb;
+import io.avaje.jsonb.spi.Generated;
+
+@Generated("io.avaje.jsonb.generator")
+public final class WrapJsonObjectJsonAdapter implements JsonAdapter, ViewBuilderAware {
+
+ // naming convention Match
+ // json [io.avaje.json.node.JsonObject] name:json publicField
+
+ private final JsonAdapter jsonObjectJsonAdapter;
+ private final PropertyNames names;
+
+ public WrapJsonObjectJsonAdapter(Jsonb jsonb) {
+ this.jsonObjectJsonAdapter = jsonb.adapter(JsonObject.class);
+ this.names = jsonb.properties("json");
+ }
+
+ @Override
+ public void toJson(JsonWriter writer, WrapJsonObject _wrapJsonObject) {
+ writer.beginObject(names);
+ writer.name(0);
+ jsonObjectJsonAdapter.toJson(writer, _wrapJsonObject.json);
+ writer.endObject();
+ }
+
+ @Override
+ public WrapJsonObject fromJson(JsonReader reader) {
+ // variables to read json values into, constructor params don't need _set$ flags
+ JsonObject _val$json = null; boolean _set$json = false;
+
+ // read json
+ reader.beginObject(names);
+ while (reader.hasNextField()) {
+ final String fieldName = reader.nextField();
+ switch (fieldName) {
+ case "json":
+ _val$json = jsonObjectJsonAdapter.fromJson(reader);
+ _set$json = true;
+ break;
+
+ default:
+ reader.unmappedField(fieldName);
+ reader.skipValue();
+ }
+ }
+ reader.endObject();
+
+ // build and return WrapJsonObject
+ WrapJsonObject _$wrapJsonObject = new WrapJsonObject();
+ if (_set$json) _$wrapJsonObject.json = _val$json;
+ return _$wrapJsonObject;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean isViewBuilderAware() {
+ return true;
+ }
+
+ @Override
+ public ViewBuilderAware viewBuild() {
+ return this;
+ }
+
+ @Override
+ public void build(ViewBuilder builder, String name, MethodHandle handle) {
+ builder.beginObject(name, handle);
+ builder.add("json", jsonObjectJsonAdapter, builder.field(WrapJsonObject.class, "json"));
+ builder.endObject();
+ }
+}