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)