Skip to content

Commit 3b67223

Browse files
committed
fix to support old jackson versions too
1 parent 373bed5 commit 3b67223

File tree

3 files changed

+68
-11
lines changed

3 files changed

+68
-11
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ dependencies {
4444
compile 'com.optimizely.ab:core-api:{VERSION}'
4545
compile 'com.optimizely.ab:core-httpclient-impl:{VERSION}'
4646
// The SDK integrates with multiple JSON parsers, here we use Jackson.
47-
compile 'com.fasterxml.jackson.core:jackson-core:2.7.1'
48-
compile 'com.fasterxml.jackson.core:jackson-annotations:2.7.1'
49-
compile 'com.fasterxml.jackson.core:jackson-databind:2.7.1'
47+
compile 'com.fasterxml.jackson.core:jackson-core:2.13.5'
48+
compile 'com.fasterxml.jackson.core:jackson-annotations:2.13.5'
49+
compile 'com.fasterxml.jackson.core:jackson-databind:2.13.5'
5050
}
5151
```
5252

core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JacksonSerializer.java

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,51 @@
1919
import com.fasterxml.jackson.annotation.JsonInclude;
2020
import com.fasterxml.jackson.core.JsonProcessingException;
2121
import com.fasterxml.jackson.databind.ObjectMapper;
22-
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
2322

2423
class JacksonSerializer implements Serializer {
2524

26-
private ObjectMapper mapper =
27-
new ObjectMapper().setPropertyNamingStrategy(
28-
PropertyNamingStrategies.SNAKE_CASE);
25+
private ObjectMapper mapper = createMapper();
26+
27+
/**
28+
* Creates an ObjectMapper with snake_case naming strategy.
29+
* Supports both Jackson 2.12+ (PropertyNamingStrategies) and earlier versions (PropertyNamingStrategy).
30+
* Uses reflection to avoid compile-time dependencies on either API.
31+
*/
32+
static ObjectMapper createMapper() {
33+
ObjectMapper objectMapper = new ObjectMapper();
34+
Object namingStrategy = getSnakeCaseStrategy();
35+
36+
try {
37+
// Use setPropertyNamingStrategy method (available in all versions)
38+
objectMapper.getClass()
39+
.getMethod("setPropertyNamingStrategy",
40+
Class.forName("com.fasterxml.jackson.databind.PropertyNamingStrategy"))
41+
.invoke(objectMapper, namingStrategy);
42+
} catch (Exception e) {
43+
throw new RuntimeException("Failed to set snake_case naming strategy", e);
44+
}
45+
46+
return objectMapper;
47+
}
48+
49+
/**
50+
* Gets the snake case naming strategy, supporting both Jackson 2.12+ and earlier versions.
51+
*/
52+
private static Object getSnakeCaseStrategy() {
53+
try {
54+
// Try Jackson 2.12+ API first
55+
Class<?> strategiesClass = Class.forName("com.fasterxml.jackson.databind.PropertyNamingStrategies");
56+
return strategiesClass.getField("SNAKE_CASE").get(null);
57+
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
58+
try {
59+
// Fall back to Jackson 2.11 and earlier (deprecated but compatible)
60+
Class<?> strategyClass = Class.forName("com.fasterxml.jackson.databind.PropertyNamingStrategy");
61+
return strategyClass.getField("SNAKE_CASE").get(null);
62+
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException ex) {
63+
throw new RuntimeException("Unable to find snake_case naming strategy in Jackson", ex);
64+
}
65+
}
66+
}
2967

3068
public <T> String serialize(T payload) {
3169
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JacksonSerializerTest.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import com.fasterxml.jackson.databind.ObjectMapper;
2020

21-
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
2221
import com.optimizely.ab.event.internal.payload.EventBatch;
2322

2423
import org.junit.Test;
@@ -36,14 +35,34 @@
3635

3736
import static org.hamcrest.CoreMatchers.is;
3837
import static org.junit.Assert.assertThat;
38+
import static org.junit.Assert.assertTrue;
39+
import static org.junit.Assert.assertNotNull;
3940

4041
public class JacksonSerializerTest {
4142

4243
private JacksonSerializer serializer = new JacksonSerializer();
43-
private ObjectMapper mapper =
44-
new ObjectMapper().setPropertyNamingStrategy(
45-
PropertyNamingStrategies.SNAKE_CASE);
44+
private ObjectMapper mapper = JacksonSerializer.createMapper();
4645

46+
@Test
47+
public void createMapperSucceeds() {
48+
// Verify that createMapper() successfully creates an ObjectMapper with snake_case naming
49+
// This tests that the reflection logic works for the current Jackson version
50+
ObjectMapper testMapper = JacksonSerializer.createMapper();
51+
assertNotNull("Mapper should be created successfully", testMapper);
52+
53+
// Verify snake_case naming by serializing a simple object
54+
class TestObject {
55+
@SuppressWarnings("unused")
56+
public String getMyFieldName() { return "test"; }
57+
}
58+
59+
try {
60+
String json = testMapper.writeValueAsString(new TestObject());
61+
assertTrue("Should use snake_case naming", json.contains("my_field_name"));
62+
} catch (Exception e) {
63+
throw new RuntimeException("Failed to serialize with snake_case naming", e);
64+
}
65+
}
4766

4867
@Test
4968
public void serializeImpression() throws IOException {

0 commit comments

Comments
 (0)