Last active
November 20, 2021 14:45
-
-
Save rnkoaa/c937c6dd35c30a4e7bde52de0d02f4d3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
plugins { | |
id 'java' | |
id 'application' | |
} | |
group 'org.example' | |
version '1.0-SNAPSHOT' | |
repositories { | |
mavenLocal() | |
mavenCentral() | |
} | |
application { | |
mainClass = 'com.richard.simple.MainApplication' | |
} | |
dependencies { | |
implementation("io.vavr:vavr:0.10.4") | |
implementation("io.projectreactor:reactor-core:3.4.12") | |
annotationProcessor("org.immutables:value:2.8.2") | |
implementation("org.immutables:value:2.8.2") | |
implementation("com.fasterxml.jackson.core:jackson-core:2.13.0") | |
implementation("com.fasterxml.jackson.core:jackson-databind:2.13.0") | |
implementation("com.fasterxml.jackson.core:jackson-annotations:2.13.0") | |
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.0") | |
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1") | |
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1") | |
} | |
test { | |
useJUnitPlatform() | |
testLogging { | |
// events "passed", "skipped", "failed" | |
showStandardStreams = true | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@JsonDeserialize(using = CustomDeserializer.class) | |
public abstract class BaseClass { | |
private String commonProp; | |
} | |
// Important to override the base class' usage of CustomDeserializer which produces an infinite loop | |
@JsonDeserialize(using = JsonDeserializer.None.class) | |
public class ClassA extends BaseClass { | |
private String classAProp; | |
} | |
@JsonDeserialize(using = JsonDeserializer.None.class) | |
public class ClassB extends BaseClass { | |
private String classBProp; | |
} | |
public class CustomDeserializer extends StdDeserializer<BaseClass> { | |
protected CustomDeserializer() { | |
super(BaseClass.class); | |
} | |
@Override | |
public BaseClass deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { | |
TreeNode node = p.readValueAsTree(); | |
// Select the concrete class based on the existence of a property | |
if (node.get("classAProp") != null) { | |
return p.getCodec().treeToValue(node, ClassA.class); | |
} | |
return p.getCodec().treeToValue(node, ClassB.class); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Documented | |
@Retention(RetentionPolicy.RUNTIME) | |
@Target(ElementType.TYPE) | |
public @interface EventMetadata { | |
int version() default 1; | |
String name() default ""; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static void main(String[] args) throws JsonProcessingException { | |
var objectMapper = provideObjectMapper(); | |
var list = new VersionedEventList(); | |
var event = ImmutableProductCreatedEvent.builder() | |
.name("Hane's T-Shirts") | |
.build(); | |
list.add(event); | |
var updatedEvent = ImmutableProductUpdatedEvent.builder() | |
.active(true) | |
.name("Hane's Blue T-Shirts") | |
.build(); | |
list.add(updatedEvent); | |
System.out.println(objectMapper.writeValueAsString(event)); | |
System.out.println(objectMapper.writeValueAsString(updatedEvent)); | |
String value = objectMapper.writeValueAsString(list); | |
System.out.println(value); | |
VersionedEventList deserializedEvents = objectMapper.readValue(value, VersionedEventList.class); | |
System.out.println(deserializedEvents); | |
} | |
static ObjectMapper provideObjectMapper() { | |
var objectMapper = new ObjectMapper().findAndRegisterModules(); | |
objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategies.SnakeCaseStrategy()); | |
// Ignore null values when writing json. | |
objectMapper.setSerializationInclusion(Include.NON_DEFAULT); | |
objectMapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true); | |
// objectMapper.configure(SerializationFeature., true); | |
// Write times as a String instead of a Long so its human readable. | |
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); | |
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); | |
// objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE) | |
objectMapper.registerModule(new JavaTimeModule()); | |
return objectMapper; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; | |
import com.fasterxml.jackson.databind.annotation.JsonSerialize; | |
import com.richard.simple.base.EventMetadata; | |
import com.richard.simple.base.VersionedEvent; | |
import org.immutables.value.Value.Immutable; | |
@Immutable | |
@JsonSerialize | |
@JsonDeserialize(builder = ImmutableProductCreatedEvent.Builder.class) | |
@EventMetadata(name = "ProductCreatedEvent") | |
public interface ProductCreatedEvent extends VersionedEvent { | |
String getName(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; | |
import com.fasterxml.jackson.databind.annotation.JsonSerialize; | |
import com.richard.simple.base.EventMetadata; | |
import com.richard.simple.base.VersionedEvent; | |
import org.immutables.value.Value.Immutable; | |
@Immutable | |
@JsonSerialize | |
@JsonDeserialize(builder = ImmutableProductUpdatedEvent.Builder.class) | |
@EventMetadata(name = "ProductUpdatedEvent") | |
public interface ProductUpdatedEvent extends VersionedEvent { | |
String getName(); | |
boolean active(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.richard.simple.base; | |
import com.fasterxml.jackson.annotation.JsonTypeInfo; | |
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver; | |
import java.time.Instant; | |
import java.util.UUID; | |
import org.immutables.value.Value.Default; | |
@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, property = "type") | |
@JsonTypeIdResolver(VersionedEventTypeIdResolver.class) | |
public interface VersionedEvent { | |
default UUID getId() { | |
return UUID.randomUUID(); | |
} | |
@Default | |
default Instant getCreated() { | |
return Instant.now(); | |
} | |
@Default | |
default long getVersion() { | |
return 1L; | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.richard.simple.base.VersionedEvent; | |
import java.util.ArrayList; | |
public class VersionedEventList extends ArrayList<VersionedEvent> { | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.richard.simple.base; | |
import com.fasterxml.jackson.annotation.JsonTypeInfo; | |
import com.fasterxml.jackson.databind.DatabindContext; | |
import com.fasterxml.jackson.databind.JavaType; | |
import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase; | |
import com.fasterxml.jackson.databind.type.TypeFactory; | |
public class VersionedEventTypeIdResolver extends TypeIdResolverBase { | |
private static final ClassLoader classLoader = VersionedEventTypeIdResolver.class.getClassLoader(); | |
private JavaType mBaseType; | |
@Override | |
public void init(JavaType baseType) { | |
this.mBaseType = baseType; | |
} | |
@Override | |
public String idFromValue(Object value) { | |
return idFromValueAndType(value, value.getClass()); | |
} | |
@Override | |
public String idFromValueAndType(Object value, Class<?> suggestedType) { | |
return suggestedType.getName(); | |
} | |
@Override | |
public String idFromBaseType() { | |
return idFromValueAndType(null, mBaseType.getRawClass()); | |
} | |
@Override | |
public JavaType typeFromId(DatabindContext context, String id) { | |
Class<?> clazz; | |
try { | |
clazz = classLoader.loadClass(id); | |
} catch (ClassNotFoundException e) { | |
throw new IllegalStateException("cannot find class '" + id + "'"); | |
} | |
return TypeFactory.defaultInstance().constructSpecializedType(mBaseType, clazz); | |
} | |
@Override | |
public JsonTypeInfo.Id getMechanism() { | |
return JsonTypeInfo.Id.CUSTOM; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment