diff --git a/jr-annotation-support/pom.xml b/jr-annotation-support/pom.xml
index a0289570..46d71ecf 100644
--- a/jr-annotation-support/pom.xml
+++ b/jr-annotation-support/pom.xml
@@ -76,6 +76,14 @@
org.gradlex
gradle-module-metadata-maven-plugin
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 16
+ 16
+
+
diff --git a/jr-annotation-support/src/main/java/com/fasterxml/jackson/jr/annotationsupport/AnnotationBasedIntrospector.java b/jr-annotation-support/src/main/java/com/fasterxml/jackson/jr/annotationsupport/AnnotationBasedIntrospector.java
index 4d6ad64b..c74ca090 100644
--- a/jr-annotation-support/src/main/java/com/fasterxml/jackson/jr/annotationsupport/AnnotationBasedIntrospector.java
+++ b/jr-annotation-support/src/main/java/com/fasterxml/jackson/jr/annotationsupport/AnnotationBasedIntrospector.java
@@ -16,6 +16,7 @@
import com.fasterxml.jackson.jr.ob.impl.JSONReader;
import com.fasterxml.jackson.jr.ob.impl.JSONWriter;
import com.fasterxml.jackson.jr.ob.impl.POJODefinition;
+import com.fasterxml.jackson.jr.ob.impl.RecordsHelpers;
/**
*
@@ -91,18 +92,30 @@ protected POJODefinition introspectDefinition()
constructors = null;
} else {
constructors = new BeanConstructors(_type);
- for (Constructor> ctor : _type.getDeclaredConstructors()) {
- Class>[] argTypes = ctor.getParameterTypes();
- if (argTypes.length == 0) {
- constructors.addNoArgsConstructor(ctor);
- } else if (argTypes.length == 1) {
- Class> argType = argTypes[0];
- if (argType == String.class) {
- constructors.addStringConstructor(ctor);
- } else if (argType == Integer.class || argType == Integer.TYPE) {
- constructors.addIntConstructor(ctor);
- } else if (argType == Long.class || argType == Long.TYPE) {
- constructors.addLongConstructor(ctor);
+ if (RecordsHelpers.isRecordType(_type)) {
+ Constructor> canonical = RecordsHelpers.findCanonicalConstructor(_type);
+ constructors.addRecordConstructor(canonical);
+ for (int i = 0; i < canonical.getParameterCount(); i++) {
+ Parameter ctorParam = canonical.getParameters()[i];
+ final String explName = _findExplicitName(ctorParam);
+ if (explName != null) {
+ constructors.addRecordConstructorAlias(explName, ctorParam.getType(), i);
+ }
+ }
+ } else {
+ for (Constructor> ctor : _type.getDeclaredConstructors()) {
+ Class>[] argTypes = ctor.getParameterTypes();
+ if (argTypes.length == 0) {
+ constructors.addNoArgsConstructor(ctor);
+ } else if (argTypes.length == 1) {
+ Class> argType = argTypes[0];
+ if (argType == String.class) {
+ constructors.addStringConstructor(ctor);
+ } else if (argType == Integer.class || argType == Integer.TYPE) {
+ constructors.addIntConstructor(ctor);
+ } else if (argType == Long.class || argType == Long.TYPE) {
+ constructors.addLongConstructor(ctor);
+ }
}
}
}
diff --git a/jr-annotation-support/src/test/java/com/fasterxml/jackson/jr/annotationsupport/BasicAliasTest.java b/jr-annotation-support/src/test/java/com/fasterxml/jackson/jr/annotationsupport/BasicAliasTest.java
index 1f4d0230..7d8fdf38 100644
--- a/jr-annotation-support/src/test/java/com/fasterxml/jackson/jr/annotationsupport/BasicAliasTest.java
+++ b/jr-annotation-support/src/test/java/com/fasterxml/jackson/jr/annotationsupport/BasicAliasTest.java
@@ -3,6 +3,7 @@
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.annotation.JsonAlias;
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.jr.ob.JSON;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -34,6 +35,11 @@ public String getMiddle() {
public void setLast(String str) { last = str; }
}
+ record SnakeCaseRecord(
+ @JsonProperty("first_name") String firstName,
+ @JsonProperty("last_name") String lastName
+ ) {}
+
/*
/**********************************************************************
/* Test methods
@@ -62,4 +68,20 @@ public void testSimpleAliases() throws Exception
assertEquals("Bob", result.middle);
assertEquals("Burger", result.last);
}
+
+ public void testSnakeCaseRecordDeserialization() throws Exception
+ {
+ final String input = a2q("{ 'first_name':'John', 'last_name':'Doe' }");
+ SnakeCaseRecord result;
+
+ // First: without setting, nothing matches
+ result = JSON.std.beanFrom(SnakeCaseRecord.class, input);
+ assertNull(result.firstName());
+ assertNull(result.lastName());
+
+ // but with annotations it's all good...
+ result = JSON_WITH_ANNO.beanFrom(SnakeCaseRecord.class, input);
+ assertEquals("John", result.firstName());
+ assertEquals("Doe", result.lastName());
+ }
}
diff --git a/jr-annotation-support/src/test/java/com/fasterxml/jackson/jr/annotationsupport/BasicIgnoralTest.java b/jr-annotation-support/src/test/java/com/fasterxml/jackson/jr/annotationsupport/BasicIgnoralTest.java
index ac660b1a..86f27361 100644
--- a/jr-annotation-support/src/test/java/com/fasterxml/jackson/jr/annotationsupport/BasicIgnoralTest.java
+++ b/jr-annotation-support/src/test/java/com/fasterxml/jackson/jr/annotationsupport/BasicIgnoralTest.java
@@ -4,6 +4,7 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.jr.ob.JSON;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -47,6 +48,11 @@ public XYZ(int x, int y, int z) {
}
}
+ record SnakeCaseRecord(
+ @JsonIgnore int x,
+ @JsonProperty("y_value") int y
+ ) {}
+
private final JSON JSON_WITH_ANNO = jsonWithAnnotationSupport();
private final JSON JSON_WITH_ANNO_WITH_STATIC =
JSON.builder().register(JacksonAnnotationExtension.std).enable(JSON.Feature.INCLUDE_STATIC_FIELDS).build();
@@ -118,6 +124,23 @@ public void testPropertyIgnoreWithUnknown() throws Exception
assertEquals(new XY().x, result.x);
}
+// Probably another bug that needs to be fixed. First I'm focusing on the deserialization of records.
+//
+// public void testSnakeCaseRecordDeserialization() throws Exception
+// {
+// final String input = a2q("{ 'x':1, 'y_value':2 }");
+// SnakeCaseRecord result;
+//
+// // First: without setting, nothing matches
+// result = JSON.std.beanFrom(SnakeCaseRecord.class, input);
+// assertNull(result);
+//
+// // but with annotations it's all good...
+// result = JSON_WITH_ANNO.beanFrom(SnakeCaseRecord.class, input);
+// assertEquals(0, result.x());
+// assertEquals(2, result.y());
+// }
+
/*
/**********************************************************************
/* Tests for @JsonIgnoreProperties
diff --git a/jr-annotation-support/src/test/java/com/fasterxml/jackson/jr/annotationsupport/BasicRenameTest.java b/jr-annotation-support/src/test/java/com/fasterxml/jackson/jr/annotationsupport/BasicRenameTest.java
index 971b7d9e..94ad1e7e 100644
--- a/jr-annotation-support/src/test/java/com/fasterxml/jackson/jr/annotationsupport/BasicRenameTest.java
+++ b/jr-annotation-support/src/test/java/com/fasterxml/jackson/jr/annotationsupport/BasicRenameTest.java
@@ -116,7 +116,10 @@ public static SubclassingEnum from(String id) {
}
}
-
+ record SnakeCaseRecord(
+ @JsonProperty("first_name") String firstName,
+ @JsonProperty("last_name") String lastName
+ ) {}
/*
/**********************************************************************
diff --git a/jr-annotation-support/src/test/java/com/fasterxml/jackson/jr/annotationsupport/BasicReorderTest.java b/jr-annotation-support/src/test/java/com/fasterxml/jackson/jr/annotationsupport/BasicReorderTest.java
index d35f9e77..414fb73b 100644
--- a/jr-annotation-support/src/test/java/com/fasterxml/jackson/jr/annotationsupport/BasicReorderTest.java
+++ b/jr-annotation-support/src/test/java/com/fasterxml/jackson/jr/annotationsupport/BasicReorderTest.java
@@ -1,7 +1,7 @@
package com.fasterxml.jackson.jr.annotationsupport;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
-
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.jr.ob.JSON;
import org.junit.jupiter.api.Test;
@@ -40,6 +40,11 @@ public FullNameBean(String f, String m, String l) {
public void setLast(String n) { last = n; }
}
+ record SnakeCaseRecord(
+ @JsonProperty("first_name") String firstName,
+ @JsonProperty("last_name") String lastName
+ ) {}
+
private final JSON JSON_WITH_ANNO = jsonWithAnnotationSupport();
@Test
@@ -73,4 +78,20 @@ public void testPartialReorder() throws Exception
// and ensure no leakage to default one:
assertEquals(EXP_DEFAULT, JSON.std.asString(input));
}
+
+ public void testSnakeCaseRecordDeserialization() throws Exception
+ {
+ final String input = a2q("{ 'first_name':'John', 'last_name':'Doe' }");
+ SnakeCaseRecord result;
+
+ // First: without setting, nothing matches
+ result = JSON.std.beanFrom(SnakeCaseRecord.class, input);
+ assertNull(result.firstName());
+ assertNull(result.lastName());
+
+ // but with annotations it's all good...
+ result = JSON_WITH_ANNO.beanFrom(SnakeCaseRecord.class, input);
+ assertEquals("John", result.firstName());
+ assertEquals("Doe", result.lastName());
+ }
}
diff --git a/jr-objects/pom.xml b/jr-objects/pom.xml
index 906b44db..5c3c06d7 100644
--- a/jr-objects/pom.xml
+++ b/jr-objects/pom.xml
@@ -67,6 +67,14 @@ has no other dependencies, and provides additional builder-style content generat
org.gradlex
gradle-module-metadata-maven-plugin
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 16
+ 16
+
+
diff --git a/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/BeanConstructors.java b/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/BeanConstructors.java
index 88700d59..995d5e80 100644
--- a/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/BeanConstructors.java
+++ b/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/BeanConstructors.java
@@ -1,6 +1,10 @@
package com.fasterxml.jackson.jr.ob.impl;
+import com.fasterxml.jackson.jr.ob.api.ValueReader;
+
import java.lang.reflect.Constructor;
+import java.util.HashMap;
+import java.util.Map;
/**
* Container class added to encapsulate details of collection and use of
@@ -10,6 +14,9 @@
*/
public class BeanConstructors
{
+ public record RecordAlias(ValueReader reader, Integer pos) {
+
+ }
protected final Class> _valueType;
protected Constructor> _noArgsCtor;
@@ -21,10 +28,17 @@ public class BeanConstructors
*/
protected Constructor> _recordCtor;
+ /**
+ * Aliases for Record constructor parameters.
+ *
+ * @since 2.20
+ */
+ protected Map _recordCtorAliases = new HashMap<>();
+
protected Constructor> _intCtor;
+
protected Constructor> _longCtor;
protected Constructor> _stringCtor;
-
public BeanConstructors(Class> valueType) {
_valueType = valueType;
}
@@ -42,6 +56,14 @@ public BeanConstructors addRecordConstructor(Constructor> ctor) {
return this;
}
+ /**
+ * @since 2.20
+ */
+ public BeanConstructors addRecordConstructorAlias(String alias, Class> valueType, int pos) {
+ _recordCtorAliases.put(alias, new RecordAlias(new SimpleValueReader(valueType, 9), pos)); // TODO: Should not always be a simplevaluereader
+ return this;
+ }
+
public BeanConstructors addIntConstructor(Constructor> ctor) {
_intCtor = ctor;
return this;
@@ -57,6 +79,14 @@ public BeanConstructors addStringConstructor(Constructor> ctor) {
return this;
}
+ public RecordAlias getRecordCtorAliasValueReader(String name) {
+ return _recordCtorAliases.get(name);
+ }
+
+ public int getRecordCtorAliasesCount() {
+ return _recordCtorAliases.size();
+ }
+
public void forceAccess() {
if (_noArgsCtor != null) {
_noArgsCtor.setAccessible(true);
diff --git a/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/BeanReader.java b/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/BeanReader.java
index 5c57cfcf..0575fa05 100644
--- a/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/BeanReader.java
+++ b/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/BeanReader.java
@@ -137,10 +137,17 @@ public Object readNext(JSONReader r, JsonParser p) throws IOException
}
private Object readRecord(JSONReader r, JsonParser p) throws Exception {
- final Object[] values = new Object[_propsByName.size()];
+ final Object[] values = new Object[_propsByName.size() + _constructors.getRecordCtorAliasesCount()];
String propName;
for (; (propName = p.nextFieldName()) != null;) {
+ BeanConstructors.RecordAlias recAlias;
+ if ((recAlias = _constructors.getRecordCtorAliasValueReader(propName)) != null) {
+ values[recAlias.pos()] = recAlias.reader().readNext(r, p);
+
+ continue;
+ }
+
BeanPropertyReader prop = findProperty(propName);
if (prop == null) {
handleUnknown(r, p, propName);
diff --git a/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/RecordsHelpers.java b/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/RecordsHelpers.java
index 0b2893c2..c005f094 100644
--- a/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/RecordsHelpers.java
+++ b/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/RecordsHelpers.java
@@ -37,7 +37,7 @@ public final class RecordsHelpers {
}
private RecordsHelpers() {}
- static Constructor> findCanonicalConstructor(Class> beanClass) {
+ public static Constructor> findCanonicalConstructor(Class> beanClass) {
// sanity check: caller shouldn't rely on it
if (!supportsRecords || !isRecordType(beanClass)) {
return null;
@@ -78,7 +78,7 @@ static boolean isRecordConstructor(Class> beanClass, Constructor> ctor,
}
}
- static boolean isRecordType(Class> cls) {
+ public static boolean isRecordType(Class> cls) {
Class> parent = cls.getSuperclass();
return (parent != null) && "java.lang.Record".equals(parent.getName());
}