From 3dffee5d78dc61360307e5c089c4c7adccbcf435 Mon Sep 17 00:00:00 2001 From: seregamorph Date: Tue, 23 Nov 2021 17:50:18 +0100 Subject: [PATCH] #3331 - fix default CharSequence serialization --- .../databind/ser/BeanSerializerFactory.java | 24 +++++++ .../jdk/CharSequenceSerializationTest.java | 64 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 src/test/java/com/fasterxml/jackson/databind/ser/jdk/CharSequenceSerializationTest.java diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java index 65b822039e..0f8f23d5de 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java @@ -637,6 +637,8 @@ protected List findBeanProperties(SerializerProvider prov, protected List filterBeanProperties(SerializationConfig config, BeanDescription beanDesc, List props) { + removeDefaultProperties(props); + // 01-May-2016, tatu: Which base type to use here gets tricky, since // it may often make most sense to use general type for overrides, // but what we have here may be more specific impl type. But for now @@ -767,6 +769,28 @@ protected void removeSetterlessGetters(SerializationConfig config, BeanDescripti } } + /** + * Helper method that will remove all properties that are added by default JDK methods, + * e.g. java.lang.CharSequence.isEmpty() (since JDK 15). + */ + private void removeDefaultProperties(List properties) + { + Iterator it = properties.iterator(); + while (it.hasNext()) { + BeanPropertyWriter writer = it.next(); + + AnnotatedMember member = writer.getMember(); + if (member != null) { + Class declaringClass = member.getDeclaringClass(); + + if (declaringClass == CharSequence.class) { + // #3331 JDK 15 defines CharSequence.isEmpty() method that is treated as property + it.remove(); + } + } + } + } + /** * Helper method called to ensure that we do not have "duplicate" type ids. * Added to resolve [databind#222] diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/CharSequenceSerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/CharSequenceSerializationTest.java new file mode 100644 index 0000000000..1bdf676d0a --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/CharSequenceSerializationTest.java @@ -0,0 +1,64 @@ +package com.fasterxml.jackson.databind.ser.jdk; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class CharSequenceSerializationTest { + + private static final String APP_ID = "3074457345618296002"; + + @Test + public void objectMapperShouldSerializeAsJsonStringValue() throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + + AppId appId = AppId.valueOf(APP_ID); + + String serialized = objectMapper.writeValueAsString(appId); + + //Without a fix fails on JDK17 with + //org.junit.ComparisonFailure: + //Expected :{"empty":false} + //Actual :"3074457345618296002" + assertEquals(serialized, "\"" + APP_ID + "\""); + } + + public static final class AppId implements CharSequence { + + private final long value; + + public AppId(long value) throws IllegalArgumentException { + this.value = value; + } + + public static AppId valueOf(String value) throws IllegalArgumentException { + if (value == null) { + throw new IllegalArgumentException("value is null"); + } + return new AppId(Long.parseLong(value)); + } + + @Override + public int length() { + return toString().length(); + } + + @Override + public char charAt(int index) { + return toString().charAt(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + return toString().subSequence(start, end); + } + + // pay attention: no @JsonValue here + @Override + public String toString() { + return Long.toString(value); + } + } + +}