Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .github/workflows/java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,18 @@ jobs:
strategy:
fail-fast: false
matrix:
jdk: [8, 9]
# github not support JVM 8 anymore
jdk: [11, 17]
optimizely_default_parser: [GSON_CONFIG_PARSER, JACKSON_CONFIG_PARSER, JSON_CONFIG_PARSER, JSON_SIMPLE_CONFIG_PARSER]
steps:
- name: checkout
uses: actions/checkout@v4

- name: set up JDK ${{ matrix.jdk }}
uses: AdoptOpenJDK/install-jdk@v1
uses: actions/setup-java@v4
with:
version: ${{ matrix.jdk }}
architecture: x64
java-version: ${{ matrix.jdk }}
distribution: 'temurin'

- name: Grant execute permission for gradlew
run: chmod +x gradlew
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ dependencies {
compile 'com.optimizely.ab:core-api:{VERSION}'
compile 'com.optimizely.ab:core-httpclient-impl:{VERSION}'
// The SDK integrates with multiple JSON parsers, here we use Jackson.
compile 'com.fasterxml.jackson.core:jackson-core:2.7.1'
compile 'com.fasterxml.jackson.core:jackson-annotations:2.7.1'
compile 'com.fasterxml.jackson.core:jackson-databind:2.7.1'
compile 'com.fasterxml.jackson.core:jackson-core:2.13.5'
compile 'com.fasterxml.jackson.core:jackson-annotations:2.13.5'
compile 'com.fasterxml.jackson.core:jackson-databind:2.13.5'
}
```

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
*
* Copyright 2016-2017, 2019, Optimizely and contributors
* Copyright 2016-2017, 2019, 2025 Optimizely and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,13 +19,41 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;

class JacksonSerializer implements Serializer {

private ObjectMapper mapper =
new ObjectMapper().setPropertyNamingStrategy(
PropertyNamingStrategy.SNAKE_CASE);
private ObjectMapper mapper = createMapper();

/**
* Creates an ObjectMapper with snake_case naming strategy.
* Supports both Jackson 2.12+ (PropertyNamingStrategies) and earlier versions (PropertyNamingStrategy).
* Uses reflection to avoid compile-time dependencies on either API.
*/
static ObjectMapper createMapper() {
ObjectMapper objectMapper = new ObjectMapper();
Object namingStrategy = getSnakeCaseStrategy();
objectMapper.setPropertyNamingStrategy((com.fasterxml.jackson.databind.PropertyNamingStrategy) namingStrategy);
return objectMapper;
}

/**
* Gets the snake case naming strategy, supporting both Jackson 2.12+ and earlier versions.
*/
private static Object getSnakeCaseStrategy() {
try {
// Try Jackson 2.12+ API first
Class<?> strategiesClass = Class.forName("com.fasterxml.jackson.databind.PropertyNamingStrategies");
return strategiesClass.getField("SNAKE_CASE").get(null);
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
try {
// Fall back to Jackson 2.11 and earlier (deprecated but compatible)
Class<?> strategyClass = Class.forName("com.fasterxml.jackson.databind.PropertyNamingStrategy");
return strategyClass.getField("SNAKE_CASE").get(null);
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException ex) {
throw new RuntimeException("Unable to find snake_case naming strategy in Jackson", ex);
}
}
}

public <T> String serialize(T payload) {
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import com.fasterxml.jackson.databind.ObjectMapper;

import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.optimizely.ab.event.internal.payload.EventBatch;

import org.junit.Test;
Expand All @@ -36,14 +35,34 @@

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertNotNull;

public class JacksonSerializerTest {

private JacksonSerializer serializer = new JacksonSerializer();
private ObjectMapper mapper =
new ObjectMapper().setPropertyNamingStrategy(
PropertyNamingStrategy.SNAKE_CASE);
private ObjectMapper mapper = JacksonSerializer.createMapper();

@Test
public void createMapperSucceeds() {
// Verify that createMapper() successfully creates an ObjectMapper with snake_case naming
// This tests that the reflection logic works for the current Jackson version
ObjectMapper testMapper = JacksonSerializer.createMapper();
assertNotNull("Mapper should be created successfully", testMapper);

// Verify snake_case naming by serializing a simple object
class TestObject {
@SuppressWarnings("unused")
public String getMyFieldName() { return "test"; }
}

try {
String json = testMapper.writeValueAsString(new TestObject());
assertTrue("Should use snake_case naming", json.contains("my_field_name"));
} catch (Exception e) {
throw new RuntimeException("Failed to serialize with snake_case naming", e);
}
}

@Test
public void serializeImpression() throws IOException {
Expand Down
6 changes: 5 additions & 1 deletion java-quickstart/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ dependencies {
implementation project(':core-api')
implementation project(':core-httpclient-impl')

implementation group: 'com.google.code.gson', name: 'gson', version: gsonVersion
// implementation group: 'com.google.code.gson', name: 'gson', version: gsonVersion
implementation 'com.fasterxml.jackson.core:jackson-core:2.17.0'
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.0'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.0'

implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: httpClientVersion
implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: log4jVersion
implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: log4jVersion
Expand Down