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