diff --git a/json-node/src/main/java/io/avaje/json/node/JsonNode.java b/json-node/src/main/java/io/avaje/json/node/JsonNode.java index f0dc0500..e9832325 100644 --- a/json-node/src/main/java/io/avaje/json/node/JsonNode.java +++ b/json-node/src/main/java/io/avaje/json/node/JsonNode.java @@ -109,6 +109,15 @@ default String extract(String path, String missingValue) { throw new UnsupportedOperationException(); } + /** + * Extract the int from the given path if present or the given default value. + * + * @param missingValue The value to use when the path is missing. + */ + default int extract(String path, int missingValue) { + throw new UnsupportedOperationException(); + } + /** * Extract the long from the given path if present or the given default value. * diff --git a/json-node/src/main/java/io/avaje/json/node/JsonNodeMapper.java b/json-node/src/main/java/io/avaje/json/node/JsonNodeMapper.java index 78bc0b33..69c003ed 100644 --- a/json-node/src/main/java/io/avaje/json/node/JsonNodeMapper.java +++ b/json-node/src/main/java/io/avaje/json/node/JsonNodeMapper.java @@ -40,6 +40,15 @@ static Builder builder() { return new NodeAdapterBuilder(); } + /** + * Create a NodeMapper for the specific type given the JsonAdapter. + * + * @param customAdapter The type specific JsonAdapter to use. + * @param The specific custom type being mapped. + * @return The type specific NodeMapper. + */ + NodeMapper mapper(JsonAdapter customAdapter); + /** * Return a NodeMapper for ANY json content. *

@@ -80,8 +89,8 @@ static Builder builder() { * var asJson = mapper.toJson(jsonArray); * } * - * @see NodeMapper#toJson(JsonNode, OutputStream) - * @see NodeMapper#toJson(JsonNode, Writer) + * @see NodeMapper#toJson(Object, OutputStream) + * @see NodeMapper#toJson(Object, Writer) */ String toJson(JsonNode node); diff --git a/json-node/src/main/java/io/avaje/json/node/JsonObject.java b/json-node/src/main/java/io/avaje/json/node/JsonObject.java index 85307ed4..ffb626e0 100644 --- a/json-node/src/main/java/io/avaje/json/node/JsonObject.java +++ b/json-node/src/main/java/io/avaje/json/node/JsonObject.java @@ -205,6 +205,14 @@ public String extract(String path, String missingValue) { return name == null ? missingValue : name.text(); } + @Override + public int extract(String path, int missingValue) { + final var node = find(path); + return !(node instanceof JsonNumber) + ? missingValue + : ((JsonNumber) node).intValue(); + } + @Override public long extract(String path, long missingValue) { final var node = find(path); diff --git a/json-node/src/main/java/io/avaje/json/node/NodeMapper.java b/json-node/src/main/java/io/avaje/json/node/NodeMapper.java index cb8d9fda..e2ecd527 100644 --- a/json-node/src/main/java/io/avaje/json/node/NodeMapper.java +++ b/json-node/src/main/java/io/avaje/json/node/NodeMapper.java @@ -18,7 +18,7 @@ * @see JsonNodeMapper#objectMapper() * @see JsonNodeMapper#nodeMapper() */ -public interface NodeMapper { +public interface NodeMapper { /** * Read the return the value from the json content. 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 e1bfe5e5..5a07bb5d 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 @@ -29,6 +29,11 @@ final class DJsonNodeMapper implements JsonNodeMapper { this.arrayAdapter = arrayAdapter; } + @Override + public NodeMapper mapper(JsonAdapter customAdapter) { + return new DMapper<>(customAdapter, jsonStream); + } + @Override public NodeMapper nodeMapper() { return new DMapper<>(nodeAdapter, jsonStream); diff --git a/json-node/src/main/java/io/avaje/json/node/adapter/DMapper.java b/json-node/src/main/java/io/avaje/json/node/adapter/DMapper.java index ca075449..0119c4cd 100644 --- a/json-node/src/main/java/io/avaje/json/node/adapter/DMapper.java +++ b/json-node/src/main/java/io/avaje/json/node/adapter/DMapper.java @@ -12,7 +12,7 @@ import java.io.*; -final class DMapper implements NodeMapper { +final class DMapper implements NodeMapper { private final JsonAdapter adapter; private final JsonStream jsonStream; diff --git a/json-node/src/test/java/io/avaje/json/node/CustomAdapterTest.java b/json-node/src/test/java/io/avaje/json/node/CustomAdapterTest.java new file mode 100644 index 00000000..d00b60a1 --- /dev/null +++ b/json-node/src/test/java/io/avaje/json/node/CustomAdapterTest.java @@ -0,0 +1,63 @@ +package io.avaje.json.node; + +import io.avaje.json.JsonAdapter; +import io.avaje.json.JsonReader; +import io.avaje.json.JsonWriter; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class CustomAdapterTest { + + static final JsonNodeMapper mapper = JsonNodeMapper.builder().build(); + + @Test + void mapUsingCustomAdapter() { + + MyAdapter myAdapter = new MyAdapter(mapper); + + NodeMapper typeMapper = mapper.mapper(myAdapter); + + MyCustomType source = new MyCustomType(); + source.foo = "hi"; + source.bar = 42; + String asJson = typeMapper.toJson(source); + + MyCustomType fromJson = typeMapper.fromJson(asJson); + + assertThat(fromJson.foo).isEqualTo(source.foo); + assertThat(fromJson.bar).isEqualTo(source.bar); + } + + static class MyCustomType { + public String foo; + public int bar; + } + + static class MyAdapter implements JsonAdapter { + + final NodeMapper objectMapper; + + public MyAdapter(JsonNodeMapper mapper) { + this.objectMapper = mapper.objectMapper(); + } + + @Override + public void toJson(JsonWriter writer, MyCustomType value) { + var jsonObject = JsonObject.create() + .add("foo", value.foo) + .add("bar", value.bar); + objectMapper.toJson(jsonObject, writer); + } + + @Override + public MyCustomType fromJson(JsonReader reader) { + JsonObject jsonObject = objectMapper.fromJson(reader); + + MyCustomType myCustomType = new MyCustomType(); + myCustomType.foo = jsonObject.extract("foo"); + myCustomType.bar = jsonObject.extract("bar", 0); + return myCustomType; + } + } +}