Skip to content

Commit 42131c7

Browse files
authored
GEODE-8256: Support Java 8 types in PdxInstanceImpl typed JSON deserialization (#7998)
1 parent a8e45be commit 42131c7

3 files changed

Lines changed: 164 additions & 0 deletions

File tree

geode-core/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ dependencies {
222222

223223
//Jackson databind is used in gfsh, and also in pdx
224224
implementation('com.fasterxml.jackson.core:jackson-databind')
225+
implementation('com.fasterxml.jackson.datatype:jackson-datatype-jdk8')
226+
implementation('com.fasterxml.jackson.datatype:jackson-datatype-jsr310')
225227

226228
//Commons validator is used to validate inet addresses in membership
227229
implementation('commons-validator:commons-validator')

geode-core/src/integrationTest/java/org/apache/geode/pdx/JSONFormatterJUnitTest.java

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,15 @@
1919
import static org.junit.Assert.assertTrue;
2020

2121
import java.text.SimpleDateFormat;
22+
import java.time.LocalDate;
2223
import java.util.List;
24+
import java.util.Objects;
25+
import java.util.Optional;
2326

2427
import com.fasterxml.jackson.databind.DeserializationFeature;
2528
import com.fasterxml.jackson.databind.ObjectMapper;
29+
import com.fasterxml.jackson.databind.node.ObjectNode;
30+
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
2631
import org.junit.After;
2732
import org.junit.Before;
2833
import org.junit.Test;
@@ -152,6 +157,44 @@ public void verifyJsonToPdxInstanceConversionWithJSONFormatter()
152157
actualTestObject);
153158
}
154159

160+
@Test
161+
public void verifyJsonToPdxInstanceConversionWithOptionalField() {
162+
OptionalValueHolder expectedValue = new OptionalValueHolder(Optional.of("typed-json"));
163+
String json = jsonWithType(OptionalValueHolder.class, "\"optionalValue\":\"typed-json\"");
164+
165+
PdxInstance pdxInstance = JSONFormatter.fromJSON(json);
166+
Object actualValue = pdxInstance.getObject();
167+
168+
assertEquals(OptionalValueHolder.class, actualValue.getClass());
169+
assertEquals(expectedValue, actualValue);
170+
}
171+
172+
@Test
173+
public void verifyJsonToPdxInstanceConversionWithLocalDateField() {
174+
LocalDateValueHolder expectedValue = new LocalDateValueHolder(LocalDate.of(2026, 3, 29));
175+
String json = jsonWithType(LocalDateValueHolder.class, "\"localDateValue\":\"2026-03-29\"");
176+
177+
PdxInstance pdxInstance = JSONFormatter.fromJSON(json);
178+
Object actualValue = pdxInstance.getObject();
179+
180+
assertEquals(LocalDateValueHolder.class, actualValue.getClass());
181+
assertEquals(expectedValue, actualValue);
182+
}
183+
184+
@Test
185+
public void geodePdxInstanceObjectMapperCanDeserializeJava8Types() {
186+
TimedType expectedValue = new TimedType(LocalDate.of(2026, 3, 29));
187+
188+
ObjectNode objectNode = java8ObjectMapper().valueToTree(expectedValue);
189+
objectNode.put("@type", TimedType.class.getName());
190+
191+
PdxInstance pdxInstance = JSONFormatter.fromJSON(objectNode.toString());
192+
Object actualValue = pdxInstance.getObject();
193+
194+
assertEquals(TimedType.class, actualValue.getClass());
195+
assertEquals(expectedValue, actualValue);
196+
}
197+
155198
/**
156199
* this test validates json document, where field has value and null Then it verifies we create
157200
* only one pdx type id for that
@@ -220,4 +263,119 @@ public void testJSONStringSortedFields() {
220263
System.setProperty(JSONFormatter.SORT_JSON_FIELD_NAMES_PROPERTY, "false");
221264
}
222265
}
266+
267+
private static String jsonWithType(Class<?> type, String fields) {
268+
return "{\"@type\":\"" + type.getName() + "\"," + fields + "}";
269+
}
270+
271+
private static ObjectMapper java8ObjectMapper() {
272+
ObjectMapper objectMapper = new ObjectMapper();
273+
objectMapper.registerModule(new JavaTimeModule());
274+
return objectMapper;
275+
}
276+
277+
public static class OptionalValueHolder {
278+
private Optional<String> optionalValue;
279+
280+
public OptionalValueHolder() {}
281+
282+
OptionalValueHolder(Optional<String> optionalValue) {
283+
this.optionalValue = optionalValue;
284+
}
285+
286+
public Optional<String> getOptionalValue() {
287+
return optionalValue;
288+
}
289+
290+
public void setOptionalValue(Optional<String> optionalValue) {
291+
this.optionalValue = optionalValue;
292+
}
293+
294+
@Override
295+
public boolean equals(Object object) {
296+
if (this == object) {
297+
return true;
298+
}
299+
if (!(object instanceof OptionalValueHolder)) {
300+
return false;
301+
}
302+
OptionalValueHolder that = (OptionalValueHolder) object;
303+
return Objects.equals(optionalValue, that.optionalValue);
304+
}
305+
306+
@Override
307+
public int hashCode() {
308+
return Objects.hash(optionalValue);
309+
}
310+
}
311+
312+
public static class LocalDateValueHolder {
313+
private LocalDate localDateValue;
314+
315+
public LocalDateValueHolder() {}
316+
317+
LocalDateValueHolder(LocalDate localDateValue) {
318+
this.localDateValue = localDateValue;
319+
}
320+
321+
public LocalDate getLocalDateValue() {
322+
return localDateValue;
323+
}
324+
325+
public void setLocalDateValue(LocalDate localDateValue) {
326+
this.localDateValue = localDateValue;
327+
}
328+
329+
@Override
330+
public boolean equals(Object object) {
331+
if (this == object) {
332+
return true;
333+
}
334+
if (!(object instanceof LocalDateValueHolder)) {
335+
return false;
336+
}
337+
LocalDateValueHolder that = (LocalDateValueHolder) object;
338+
return Objects.equals(localDateValue, that.localDateValue);
339+
}
340+
341+
@Override
342+
public int hashCode() {
343+
return Objects.hash(localDateValue);
344+
}
345+
}
346+
347+
public static class TimedType {
348+
private LocalDate localDate;
349+
350+
public TimedType() {}
351+
352+
TimedType(LocalDate localDate) {
353+
this.localDate = localDate;
354+
}
355+
356+
public LocalDate getLocalDate() {
357+
return localDate;
358+
}
359+
360+
public void setLocalDate(LocalDate localDate) {
361+
this.localDate = localDate;
362+
}
363+
364+
@Override
365+
public boolean equals(Object object) {
366+
if (this == object) {
367+
return true;
368+
}
369+
if (!(object instanceof TimedType)) {
370+
return false;
371+
}
372+
TimedType timedType = (TimedType) object;
373+
return Objects.equals(localDate, timedType.localDate);
374+
}
375+
376+
@Override
377+
public int hashCode() {
378+
return Objects.hash(localDate);
379+
}
380+
}
223381
}

geode-core/src/main/java/org/apache/geode/pdx/internal/PdxInstanceImpl.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828

2929
import com.fasterxml.jackson.databind.DeserializationFeature;
3030
import com.fasterxml.jackson.databind.ObjectMapper;
31+
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
32+
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
3133
import org.apache.commons.lang3.StringUtils;
3234

3335
import org.apache.geode.InternalGemFireException;
@@ -71,6 +73,8 @@ private static ObjectMapper createObjectMapper() {
7173
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
7274
mapper.configure(com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES,
7375
true);
76+
mapper.registerModule(new Jdk8Module());
77+
mapper.registerModule(new JavaTimeModule());
7478
return mapper;
7579
}
7680

0 commit comments

Comments
 (0)