loc
// getter property as simulated read-only field with getter method
final var frequency = frequency(propertyPrism.value());
final var reader = new FieldReader(element, namingConvention, currentSubType, genericTypeParams, frequency);
- reader.getterMethod(new MethodReader(methodElement, type));
+ reader.getterMethod(new MethodReader(methodElement));
localFields.add(reader);
});
}
@@ -364,6 +428,9 @@ MethodReader constructor() {
}
private MethodReader determineConstructor() {
+ if (constructor != null) {
+ return constructor;
+ }
if (defaultPublicConstructor && !allSetterMethods.isEmpty()) {
return null;
}
diff --git a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/package-info.java b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/package-info.java
index fcce3acf..a594e26e 100644
--- a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/package-info.java
+++ b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/package-info.java
@@ -3,6 +3,7 @@
@GeneratePrism(io.avaje.jsonb.Json.Import.class)
@GeneratePrism(value = io.avaje.jsonb.Json.Import.List.class, name = "ImportListPrism")
@GeneratePrism(io.avaje.jsonb.Json.Alias.class)
+@GeneratePrism(io.avaje.jsonb.Json.Creator.class)
@GeneratePrism(io.avaje.jsonb.Json.JsonAlias.class)
@GeneratePrism(io.avaje.jsonb.Json.Ignore.class)
@GeneratePrism(io.avaje.jsonb.Json.Property.class)
diff --git a/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/Kingfisher.java b/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/Kingfisher.java
new file mode 100644
index 00000000..e4808b41
--- /dev/null
+++ b/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/Kingfisher.java
@@ -0,0 +1,18 @@
+package io.avaje.jsonb.generator.models.valid;
+
+public class Kingfisher {
+ private String name;
+ private int fishCaught;
+
+ public String getName() {
+ return name;
+ }
+
+ public int getFishCaught() {
+ return fishCaught;
+ }
+
+ public void setFishCaught(int fishCaught) {
+ this.fishCaught = fishCaught;
+ }
+}
diff --git a/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/KingfisherMixin.java b/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/KingfisherMixin.java
new file mode 100644
index 00000000..de00fa9d
--- /dev/null
+++ b/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/KingfisherMixin.java
@@ -0,0 +1,13 @@
+package io.avaje.jsonb.generator.models.valid;
+
+import io.avaje.jsonb.Json;
+import io.avaje.jsonb.Json.MixIn;
+
+@MixIn(Kingfisher.class)
+public interface KingfisherMixin {
+
+ @Json.Creator
+ static Kingfisher construct(String name) {
+ return null;
+ }
+}
diff --git a/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/Student.java b/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/Student.java
new file mode 100644
index 00000000..bf740a98
--- /dev/null
+++ b/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/Student.java
@@ -0,0 +1,32 @@
+package io.avaje.jsonb.generator.models.valid;
+
+import io.avaje.jsonb.Json;
+
+@Json
+public class Student {
+ private final String name;
+ private int rollNo;
+
+ @Json.Creator
+ public Student(@Json.Alias("theName") String name, long rolling) {
+ this.name = name;
+ this.rollNo = name.length();
+ }
+
+ public Student(String name, int rollNo) {
+ this.name = name;
+ this.rollNo = rollNo;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getRollNo() {
+ return rollNo;
+ }
+
+ public void setRollNo(int rollNo) {
+ this.rollNo = rollNo;
+ }
+}
diff --git a/jsonb-inject-plugin/pom.xml b/jsonb-inject-plugin/pom.xml
index 117ee560..60dcbbc7 100644
--- a/jsonb-inject-plugin/pom.xml
+++ b/jsonb-inject-plugin/pom.xml
@@ -6,7 +6,7 @@
avaje-jsonb-parent
io.avaje
- 1.10-RC1
+ 1.10-RC2
avaje-jsonb-inject-plugin
@@ -18,7 +18,7 @@
io.avaje
avaje-jsonb
- 1.7
+ 1.10-RC1
provided
true
diff --git a/jsonb-inject-plugin/src/main/java/io/avaje/jsonb/inject/DefaultJsonbProvider.java b/jsonb-inject-plugin/src/main/java/io/avaje/jsonb/inject/DefaultJsonbProvider.java
index 9563b27f..24abe3f9 100644
--- a/jsonb-inject-plugin/src/main/java/io/avaje/jsonb/inject/DefaultJsonbProvider.java
+++ b/jsonb-inject-plugin/src/main/java/io/avaje/jsonb/inject/DefaultJsonbProvider.java
@@ -2,6 +2,7 @@
import io.avaje.inject.BeanScopeBuilder;
import io.avaje.jsonb.Jsonb;
+import io.avaje.jsonb.stream.BufferRecycleStrategy;
/** Plugin for avaje inject that provides a default Jsonb instance. */
public final class DefaultJsonbProvider implements io.avaje.inject.spi.Plugin {
@@ -24,6 +25,11 @@ public void apply(BeanScopeBuilder builder) {
.mathTypesAsString(props.equalTo("jsonb.serialize.mathTypesAsString", "true"))
.serializeEmpty(props.notEqualTo("jsonb.serialize.empty", "false"))
.serializeNulls(props.equalTo("jsonb.serialize.nulls", "true"))
+ .bufferRecycling(
+ props
+ .get("jsonb.bufferRecycling")
+ .map(BufferRecycleStrategy::valueOf)
+ .orElse(BufferRecycleStrategy.HYBRID_POOL))
.build();
});
}
diff --git a/jsonb-jackson/pom.xml b/jsonb-jackson/pom.xml
index 78b16584..1419a8fe 100644
--- a/jsonb-jackson/pom.xml
+++ b/jsonb-jackson/pom.xml
@@ -4,7 +4,7 @@
avaje-jsonb-parent
io.avaje
- 1.10-RC1
+ 1.10-RC2
avaje-jsonb-jackson
diff --git a/jsonb-spring-adapter/pom.xml b/jsonb-spring-adapter/pom.xml
index 0cdbda44..3c963dbe 100644
--- a/jsonb-spring-adapter/pom.xml
+++ b/jsonb-spring-adapter/pom.xml
@@ -6,7 +6,7 @@
avaje-jsonb-parent
io.avaje
- 1.10-RC1
+ 1.10-RC2
avaje-jsonb-spring-starter
diff --git a/jsonb/pom.xml b/jsonb/pom.xml
index c033347b..6296541e 100644
--- a/jsonb/pom.xml
+++ b/jsonb/pom.xml
@@ -4,7 +4,7 @@
io.avaje
avaje-jsonb-parent
- 1.10-RC1
+ 1.10-RC2
avaje-jsonb
diff --git a/jsonb/src/main/java/io/avaje/jsonb/Json.java b/jsonb/src/main/java/io/avaje/jsonb/Json.java
index 89947d23..e89a0f6a 100644
--- a/jsonb/src/main/java/io/avaje/jsonb/Json.java
+++ b/jsonb/src/main/java/io/avaje/jsonb/Json.java
@@ -1,10 +1,12 @@
package io.avaje.jsonb;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.MODULE;
import static java.lang.annotation.ElementType.PACKAGE;
+import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.CLASS;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -130,7 +132,7 @@
* }
*/
@Retention(CLASS)
- @Target(FIELD)
+ @Target({FIELD, PARAMETER})
@interface Alias {
/** One or more secondary names to accept as aliases to the official name. */
@@ -309,6 +311,41 @@
Class> value();
}
+ /**
+ * Marker annotation that can be used to define constructors or factory methods as one to use for instantiating new instances of the associated class. Can be used in Mixin classes to override an existing deserialization method
+ *
+ * The parameter names will be used as keys for deserialization instead of the field names.
+ *
+ * Examples:
+ *
+ * {@code
+ *
+ * @Json
+ * public class Kingfisher {
+ *
+ * @Json.Creator
+ * public Kingfisher(String name) {
+ * ...
+ * }
+ * ...
+ *
+ * }
+ *
+ * {@code
+ *
+ * @Json
+ * public record Product( ... ) {
+ *
+ * public static Product factory(@Json.Alias("alias") String name){
+ * ...
+ * }
+ *
+ * }
+ */
+ @Retention(CLASS)
+ @Target({CONSTRUCTOR, METHOD})
+ public @interface Creator {}
+
/**
* The naming convention that we can use for a given type.
*/
diff --git a/jsonb/src/main/java/io/avaje/jsonb/JsonReader.java b/jsonb/src/main/java/io/avaje/jsonb/JsonReader.java
index 0d89be0b..7abb655f 100644
--- a/jsonb/src/main/java/io/avaje/jsonb/JsonReader.java
+++ b/jsonb/src/main/java/io/avaje/jsonb/JsonReader.java
@@ -214,30 +214,12 @@ enum Token {
*/
BEGIN_ARRAY,
-// /**
-// * The closing of a JSON array. Written using {@link JsonWriter#endArray} and read using {@link
-// * JsonReader#endArray}.
-// */
-// END_ARRAY,
-
/**
* The opening of a JSON object. Written using {@link JsonWriter#beginObject} and read using
* {@link JsonReader#beginObject}.
*/
BEGIN_OBJECT,
-// /**
-// * The closing of a JSON object. Written using {@link JsonWriter#endObject} and read using
-// * {@link JsonReader#endObject}.
-// */
-// END_OBJECT,
-//
-// /**
-// * A JSON property name. Within objects, tokens alternate between names and their values.
-// * Written using {@link JsonWriter#name} and read using {@link JsonReader#nextField()}
-// */
-// NAME,
-
/**
* A JSON string.
*/
@@ -258,10 +240,5 @@ enum Token {
*/
NULL,
-// /**
-// * The end of the JSON stream. This sentinel value is returned by {@link JsonReader#peek()} to
-// * signal that the JSON-encoded value has no more tokens.
-// */
-// END_DOCUMENT
}
}
diff --git a/jsonb/src/main/java/io/avaje/jsonb/stream/HybridBufferRecycler.java b/jsonb/src/main/java/io/avaje/jsonb/stream/HybridBufferRecycler.java
index 7a64e49c..9d2d4c0e 100644
--- a/jsonb/src/main/java/io/avaje/jsonb/stream/HybridBufferRecycler.java
+++ b/jsonb/src/main/java/io/avaje/jsonb/stream/HybridBufferRecycler.java
@@ -10,11 +10,11 @@
import java.util.function.Predicate;
/**
- * This is a custom implementation of the Jackson's RecyclerPool intended to work equally
- * well with both platform and virtual threads. This pool works regardless of the version of the JVM
- * in use and internally uses 2 distinct pools one for platform threads (which is exactly the same
- * {@link ThreadLocal} based one provided by Jackson out of the box) and the other designed for
- * being virtual threads friendly. It switches between the 2 only depending on the nature of thread
+ * This is a custom implementation of the Jackson's RecyclerPool intended to work equally well with
+ * both platform and virtual threads. This pool works regardless of the version of the JVM in use
+ * and internally uses 2 distinct pools one for platform threads (which is exactly the same {@link
+ * ThreadLocal} based one provided by Jackson out of the box) and the other designed for being
+ * virtual threads friendly. It switches between the 2 only depending on the nature of thread
* (virtual or not) requiring the acquisition of a pooled resource, obtained via {@link
* MethodHandle} to guarantee compatibility also with old JVM versions. The pool also guarantees
* that the pooled resource is always released to the same internal pool from where it has been
@@ -38,15 +38,10 @@ final class HybridBufferRecycler implements BufferRecycler {
private static final Predicate isVirtual = VirtualPredicate.findIsVirtualPredicate();
- private final BufferRecycler nativePool = ThreadLocalPool.shared();
+ private static final BufferRecycler NATIVE_RECYCLER = ThreadLocalPool.shared();
+ private static final BufferRecycler VIRTUAL_RECYCLER = StripedLockFreePool.shared();
- private static class VirtualPoolHolder {
-
- private static final StripedLockFreePool virtualPool = new StripedLockFreePool(Runtime.getRuntime().availableProcessors());
- }
-
- private HybridBufferRecycler() {
- }
+ private HybridBufferRecycler() {}
static HybridBufferRecycler shared() {
return INSTANCE;
@@ -55,40 +50,41 @@ static HybridBufferRecycler shared() {
@Override
public JsonGenerator generator(JsonOutput target) {
return isVirtual.test(Thread.currentThread())
- ? VirtualPoolHolder.virtualPool.generator(target)
- : nativePool.generator(target);
+ ? VIRTUAL_RECYCLER.generator(target)
+ : NATIVE_RECYCLER.generator(target);
}
@Override
public JsonParser parser(byte[] bytes) {
return isVirtual.test(Thread.currentThread())
- ? VirtualPoolHolder.virtualPool.parser(bytes)
- : nativePool.parser(bytes);
+ ? VIRTUAL_RECYCLER.parser(bytes)
+ : NATIVE_RECYCLER.parser(bytes);
}
@Override
public JsonParser parser(InputStream in) {
return isVirtual.test(Thread.currentThread())
- ? VirtualPoolHolder.virtualPool.parser(in)
- : nativePool.parser(in);
+ ? VIRTUAL_RECYCLER.parser(in)
+ : NATIVE_RECYCLER.parser(in);
}
@Override
public void recycle(JsonGenerator recycler) {
if (recycler instanceof VThreadJGenerator) {
- VirtualPoolHolder.virtualPool.recycle(recycler);
+ VIRTUAL_RECYCLER.recycle(recycler);
}
}
@Override
public void recycle(JsonParser recycler) {
if (recycler instanceof VThreadJParser) {
- VirtualPoolHolder.virtualPool.recycle(recycler);
+ VIRTUAL_RECYCLER.recycle(recycler);
}
}
static final class StripedLockFreePool implements BufferRecycler {
- private static final StripedLockFreePool INSTANCE = new StripedLockFreePool(Runtime.getRuntime().availableProcessors());
+ private static final StripedLockFreePool INSTANCE =
+ new StripedLockFreePool(Runtime.getRuntime().availableProcessors());
private static final int CACHE_LINE_SHIFT = 4;
@@ -244,7 +240,8 @@ private static final class VirtualPredicate {
private static MethodHandle findVirtualMH() {
try {
- return MethodHandles.publicLookup().findVirtual(Thread.class, "isVirtual", MethodType.methodType(boolean.class));
+ return MethodHandles.publicLookup()
+ .findVirtual(Thread.class, "isVirtual", MethodType.methodType(boolean.class));
} catch (Exception e) {
return null;
}
@@ -252,15 +249,21 @@ private static MethodHandle findVirtualMH() {
private static Predicate findIsVirtualPredicate() {
if (virtualMh == null) {
- return thread -> false;
+ return VirtualPredicate::notVirtual;
}
- return thread -> {
- try {
- return (boolean) virtualMh.invokeExact(thread);
- } catch (Throwable e) {
- throw new RuntimeException(e);
- }
- };
+ return VirtualPredicate::isVirtual;
+ }
+
+ private static boolean isVirtual(Thread thread) {
+ try {
+ return (boolean) virtualMh.invokeExact(thread);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static boolean notVirtual(Thread thread) {
+ return false;
}
}
diff --git a/pom.xml b/pom.xml
index 282c9434..ab007199 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
io.avaje
avaje-jsonb-parent
- 1.10-RC1
+ 1.10-RC2
pom
jsonb parent