Skip to content

Instantly share code, notes, and snippets.

@erizzo
Last active February 20, 2025 22:34
Show Gist options
  • Save erizzo/3b9d6f42c8f97a54f89c8a2e81d85c6d to your computer and use it in GitHub Desktop.
Save erizzo/3b9d6f42c8f97a54f89c8a2e81d85c6d to your computer and use it in GitHub Desktop.
ProGuard Jackson problem
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@AllArgsConstructor
@ToString
public class Aztek extends CarModel {
private int year;
@JsonCreator
public Aztek(@JsonProperty("name") String name, @JsonProperty("year") int year) {
this.year = year;
}
@Override
@ToString.Include
public String getName() {
return "Ugly";
}
}
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.guardsquare:proguard-gradle:7.6.1'
}
}
plugins {
id 'java'
id "io.freefair.lombok" version "8.12.1"
}
repositories {
mavenCentral()
}
def appMainClass = 'rizzo.test.JacksonPolymorphicSerialization'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
jar {
duplicatesStrategy = 'exclude'
manifest {
attributes 'Main-Class': "${appMainClass}"
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
dependencies {
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.18.2'
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.18.2'
}
task proguard(type: proguard.gradle.ProGuardTask) {
dependsOn classes
verbose
printmapping "${buildDir}/proguard-mapping.txt"
injars "${buildDir}/classes/java/main"
outjars "${buildDir}/classes/obfuscated/"
libraryjars "${System.getProperty('java.home')}/jmods/java.base.jmod", jarfilter: '!**.jar', filter: '!module-info.class'
libraryjars "${System.getProperty('java.home')}/jmods/java.logging.jmod", jarfilter: '!**.jar', filter: '!module-info.class'
libraryjars "${System.getProperty('java.home')}/jmods/java.desktop.jmod", jarfilter: '!**.jar', filter: '!module-info.class'
libraryjars "${System.getProperty('java.home')}/jmods/java.xml.jmod", jarfilter: '!**.jar', filter: '!module-info.class'
libraryjars "${System.getProperty('java.home')}/jmods/java.sql.jmod", jarfilter: '!**.jar', filter: '!module-info.class'
// This will contain the app dependencies.
libraryjars sourceSets.main.compileClasspath
keepdirectories
// Preserve getters and setters
keepclassmembers 'class * { \
** get*(); \
void set*(***); \
}'
// Keep the main class entry point.
keep "public class ${appMainClass} { \
public static void main(java.lang.String[]); \
}"
// This helps produce useful stack traces (see https://www.guardsquare.com/manual/configuration/examples#stacktrace)
renamesourcefileattribute 'SourceFile'
keepattributes '*Annotation*,EnclosingMethod,SourceFile,LineNumberTable'
doLast {
delete "${buildDir}/classes/java/main"
copy {
from "${buildDir}/classes/obfuscated/"
into "${buildDir}/classes/java/main"
}
}
}
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
@JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Corvette.class),
@JsonSubTypes.Type(value = Aztek.class)
})
public abstract class CarModel {
public abstract String getName();
public abstract int getYear();
}
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
@Getter
@AllArgsConstructor
@ToString
public class Corvette extends CarModel {
private int year;
@JsonCreator
public Corvette(@JsonProperty("name") String name, @JsonProperty("year") int year) {
this.year = year;
}
@Override
@ToString.Include
public String getName() {
return "Corvette";
}
}
import java.util.List;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
@Getter
public class Inventory {
private String owner;
private List<CarModel> cars;
@JsonCreator
public Inventory(@JsonProperty("owner") String owner, @JsonProperty("cars") List<CarModel> cars) {
this.owner = owner;
this.cars = cars;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("Inventory [");
builder.append("owner=")
.append(owner)
.append(", cars=<");
System.out.println("First cars element is of type " + cars.get(0).getClass());
cars.forEach(car -> builder.append(car).append(", "));
builder.append(">");
builder.append("]");
return builder.toString();
}
}
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
import static com.fasterxml.jackson.databind.MapperFeature.DEFAULT_VIEW_INCLUSION;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.json.JsonMapper;
public class JacksonPolymorphicSerialization {
private final static ObjectMapper JSONMapper =
JsonMapper.builder()
.configure(DEFAULT_VIEW_INCLUSION, false)
.configure(FAIL_ON_UNKNOWN_PROPERTIES, false)
.serializationInclusion(Include.NON_ABSENT)
.build();
private static final ObjectReader JSONReader = JSONMapper.readerFor(Inventory.class);
private static final ObjectWriter JSONWriter = JSONMapper.writerFor(Inventory.class).withDefaultPrettyPrinter();
public static void main(String[] args) throws JacksonException {
Corvette corvette = new Corvette(1963);
Aztek aztek = new Aztek(2003);
Inventory inventory = new Inventory("Jay Leno", List.of(corvette, aztek));
String json = JSONWriter.writeValueAsString(inventory);
System.out.println("Serialized form:\n" + json);
System.out.println();
inventory = JSONReader.readValue(json);
System.out.println("Deserialized object: " + inventory);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment