diff --git a/jr-objects/pom.xml b/jr-objects/pom.xml
index 335231f7..e966a445 100644
--- a/jr-objects/pom.xml
+++ b/jr-objects/pom.xml
@@ -40,6 +40,12 @@ has no other dependencies, and provides additional builder-style content generat
jackson-core
${jackson.version.core}
+
+ org.apache.groovy
+ groovy
+ 4.0.18
+ test
+
@@ -54,6 +60,21 @@ has no other dependencies, and provides additional builder-style content generat
+
+
+
+ org.codehaus.gmaven
+ gmaven-plugin
+ 1.5
+
+
+
+ testCompile
+
+
+
+
com.google.code.maven-replacer-plugin
replacer
diff --git a/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/BeanPropertyIntrospector.java b/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/BeanPropertyIntrospector.java
index 1eb5815f..375d7098 100644
--- a/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/BeanPropertyIntrospector.java
+++ b/jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/BeanPropertyIntrospector.java
@@ -116,8 +116,7 @@ private static void _introspect(Class> currType, Map prop
final int flags = m.getModifiers();
// 13-Jun-2015, tatu: Skip synthetic, bridge methods altogether, for now
// at least (add more complex handling only if absolutely necessary)
- if (Modifier.isStatic(flags)
- || m.isSynthetic() || m.isBridge()) {
+ if (Modifier.isStatic(flags) || m.isSynthetic() || m.isBridge() || isGroovyMetaClass(m.getReturnType())) {
continue;
}
Class> argTypes[] = m.getParameterTypes();
@@ -158,12 +157,7 @@ private static void _introspect(Class> currType, Map prop
}
private static PropBuilder _propFrom(Map props, String name) {
- PropBuilder prop = props.get(name);
- if (prop == null) {
- prop = Prop.builder(name);
- props.put(name, prop);
- }
- return prop;
+ return props.computeIfAbsent(name, Prop::builder);
}
private static String decap(String name) {
@@ -182,4 +176,13 @@ private static String decap(String name) {
return name;
}
+ /**
+ * Helper method to detect Groovy's problematic metadata accessor type.
+ *
+ * @implNote Groovy MetaClass have cyclic reference, and hence the class containing it should not be serialised without
+ * either removing that reference, or skipping over such references.
+ */
+ protected static boolean isGroovyMetaClass(Class> clazz) {
+ return "groovy.lang.MetaClass".equals(clazz.getName());
+ }
}
diff --git a/jr-objects/src/test/groovy/com/fasterxml/jackson/jr/GroovyTest.groovy b/jr-objects/src/test/groovy/com/fasterxml/jackson/jr/GroovyTest.groovy
new file mode 100644
index 00000000..0af7b401
--- /dev/null
+++ b/jr-objects/src/test/groovy/com/fasterxml/jackson/jr/GroovyTest.groovy
@@ -0,0 +1,62 @@
+package com.fasterxml.jackson.jr
+
+import org.junit.Test
+import org.junit.Assert
+
+import com.fasterxml.jackson.jr.ob.JSON
+import com.fasterxml.jackson.jr.ob.TestBase
+
+class GroovyTest
+{
+ @Test
+ void testSimpleGroovyObject() throws Exception {
+ def json = JSON.std.asString(new GroovyOb())
+ def expected = """{"AAAAA_A_Field_Starting_With_Two_Capital_Letters":"XYZ","aDouble":0.0,"aPublicInitializedInteger":56,"aPublicInitializedIntegerObject":1516,"aPublicUninitializedInteger":0,"anInitializedIntegerObject":1112,"anInitializedPublicString":"stringData","anInitializedString":"ABC","anInteger":0,"anIntegerWithValue":12}"""
+ Assert.assertEquals(json, expected)
+ }
+}
+
+class GroovyOb {
+ int anInteger
+ int anIntegerWithValue = 12
+
+ static int anStaticInteger = 34
+ static int anStaticIntegerWithValue = 34
+
+ public int aPublicUninitializedInteger
+ public int aPublicInitializedInteger = 56
+
+ private int aPrivateUninitializedInteger
+ private int aPrivateInitializedInteger = 78
+
+ public static int aPublicStaticUninitializedInteger
+ public static int aPublicStaticInitializedInteger = 910
+
+ Integer anIntegerObject
+ Integer anInitializedIntegerObject = 1112
+
+ static Integer aStaticIntegerObject
+ static Integer aStaticInitializedIntegerObject = 1314
+
+ public Integer aPublicUninitializedIntegerObject
+ public Integer aPublicInitializedIntegerObject = 1516
+
+ public static Integer aPublicStaticUninitializedIntegerObject
+ public static Integer aPublicStaticInitializedIntegerObject = 1718
+
+ String aString
+ String anInitializedString = "ABC"
+
+ static String aStaticString = "jacksonJR"
+
+ public String aPublicString
+ public String anInitializedPublicString = "stringData"
+
+ public String AAAAA_A_Field_Starting_With_Two_Capital_Letters = "XYZ"
+ //Other Items
+ public static String staticStr = "jacksonJR" // Public Static Object
+ static int anStaticInt // Uninitialized Static Object
+ public double aDouble // uninitialized primitive
+ public Double aDoubleObject // testing boxing object
+ private int hiddenvalue = 123 // private value
+}
diff --git a/jr-stree/src/main/java/com/fasterxml/jackson/jr/stree/util/JrsTreeTraversingParser.java b/jr-stree/src/main/java/com/fasterxml/jackson/jr/stree/util/JrsTreeTraversingParser.java
index b321d556..ab8ee04f 100644
--- a/jr-stree/src/main/java/com/fasterxml/jackson/jr/stree/util/JrsTreeTraversingParser.java
+++ b/jr-stree/src/main/java/com/fasterxml/jackson/jr/stree/util/JrsTreeTraversingParser.java
@@ -193,10 +193,16 @@ public boolean isClosed() {
*/
@Override
- public String getCurrentName() {
+ public String currentName() {
return (_nodeCursor == null) ? null : _nodeCursor.getCurrentName();
}
+ @Override
+ @Deprecated // since 2.17
+ public String getCurrentName() {
+ return currentName();
+ }
+
@Override
public void overrideCurrentName(String name)
{
@@ -211,13 +217,25 @@ public JsonStreamContext getParsingContext() {
}
@Override
+ public JsonLocation currentTokenLocation() {
+ return JsonLocation.NA;
+ }
+
+ @Override
+ @Deprecated // since 2.17
public JsonLocation getTokenLocation() {
+ return currentTokenLocation();
+ }
+
+ @Override
+ public JsonLocation currentLocation() {
return JsonLocation.NA;
}
@Override
+ @Deprecated // since 2.17
public JsonLocation getCurrentLocation() {
- return JsonLocation.NA;
+ return currentLocation();
}
/*
diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x
index d6c182ee..64d2bc62 100644
--- a/release-notes/CREDITS-2.x
+++ b/release-notes/CREDITS-2.x
@@ -23,6 +23,12 @@ Jonas Konrad (@yawkat)
* Suggested #88: Make `jr-stree` dependency to `jr-objects` optional
(2.13.0)
+Nikolay Chashnikov (@chashnikov)
+
+* Requested #93: Skip serialization of `groovy.lang.MetaClass`
+ values to avoid `StackOverflowError`
+ (2.17.0)
+
Gerben Oolbekkink (@github)
* Reported #98: `module-info.java` of `jr-stree` refers to module `com.fasterxml.jackson.jr.ob.api`,
@@ -42,5 +48,8 @@ Julian Honnen (@jhonnen)
@Shounaks
-* Contributed implf ro #100: Add support for `java.time` (Java 8 date/time) types
+* Contributed fix for #93: Skip serialization of `groovy.lang.MetaClass` values
+ to avoid `StackOverflowError`
+ (2.17.0)
+* Contributed impl for #100: Add support for `java.time` (Java 8 date/time) types
(2.17.0)
diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x
index 59199792..3fd427f5 100644
--- a/release-notes/VERSION-2.x
+++ b/release-notes/VERSION-2.x
@@ -15,6 +15,9 @@ Modules:
#78: Deserializes "null" to "0.0" for `java.lang.Double` (wrapper)
(reported by @bill-phast)
+#93: Skip serialization of `groovy.lang.MetaClass` values to avoid `StackOverflowError`
+ (requested by Nikolay C)
+ (fix contributed by @Shounaks)
#100: Add support for `java.time` (Java 8 date/time) types
(requested by @sebastian-zero)
(contributed by @Shounaks)