Почему Джексон будет пропускать информацию типа для сериализованных полиморфных типов?JAVA

Программисты JAVA общаются здесь
Anonymous
Почему Джексон будет пропускать информацию типа для сериализованных полиморфных типов?

Сообщение Anonymous »

В проекте, который запутывается с помощью Proguard (используя плагин Gradle), я имею много сериализации/десериализации с использованием Джексона. Я обнаружил, что в запутанных сборках определенные виды сериализации полиморфных типов пропускают информацию о типе (указанная с помощью @jsontypeinfo (use = id.name, include = as.property, property = "type") ), что вызывает некоторое очень странное поведение во время десериализации. < /p>
Например, вот (правильная) сериализованная форма при запуске с Неубийный код :

Код: Выделить всё

{
"owner" : "Jay Leno",
"cars" : [ {
"type" : "Corvette",
"name" : "Corvette",
"year" : 1963
}, {
"type" : "Aztek",
"name" : "Ugly",
"year" : 2003
} ]
}
< /code>
Вот вывод того же кода после прохождения через Proguard: < /p>
{
"owner" : "Jay Leno",
"cars" : [ {
"a" : 1963,
"name" : "Corvette"
}, {
"a" : 2003,
"name" : "Ugly"
} ]
}
Обратите внимание на свойство «type» отсутствует в элементах массива в запутанной форме. Это заставляет Джексона неправильно детериализовать эти объекты. < /P>
Вот MRE кода; Извините за то, что включил в вопрос столько кода, но это настолько минимально, насколько я мог бы получить, и все еще продемонстрировать контекст и проблему. Project. < /p>

Код: Выделить всё

@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();
}

@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";
}
}

@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";
}
}

@Getter
public class Inventory {

private String owner;
private List cars;

@JsonCreator
public Inventory(@JsonProperty("owner") String owner, @JsonProperty("cars") List cars) {
this.owner = owner;
this.cars = cars;
}

@Override
public String toString() {
StringBuilder builder = new StringBuilder("Inventory [");
builder.append("owner=")
.append(owner)
.append(", cars=");

builder.append("]");
return builder.toString();
}
}

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);
}
}
< /code>
Вот файл строительства Gradle < /p>
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"
}

}
}

запустить ./gradlew jar Чтобы построить, вы получите нормальную (незаметно) JAR. Запустите ./Gradlew Proguard Jar и полученная банка будет запутана. В любом случае, вы можете запустить с java -jar ...
Вот что происходит с каждым. p>

Код: Выделить всё

> java -jar build/libs/ProguardJacksonTest.jar
Serialized form:
{
"owner" : "Jay Leno",
"cars" : [ {
"type" : "Corvette",
"name" : "Corvette",
"year" : 1963
}, {
"type" : "Aztek",
"name" : "Ugly",
"year" : 2003
} ]
}

First cars element is of type class rizzo.test.Corvette
Deserialized object: Inventory [owner=Jay Leno, cars=]
< /code>
Вывод с использованием запутанной сборки: < /p>
> java -jar build/libs/ProguardJacksonTest.jar
Serialized form:
{
"owner" : "Jay Leno",
"cars" : [ {
"a" : 1963,
"name" : "Corvette"
}, {
"a" : 2003,
"name" : "Ugly"
} ]
}

Exception in thread "main" java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class rizzo.test.b (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; rizzo.test.b is in unnamed module of loader 'app')
at rizzo.test.d.toString(SourceFile:29)
at java.base/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453)
at java.base/java.lang.StringConcatHelper.simpleConcat(StringConcatHelper.java:408)
at rizzo.test.JacksonPolymorphicSerialization.main(SourceFile:38)
ClasscastException указывает, что список Cars фактически заполняется экземплярами LinkedHashmap, а не экземплярами Carmodel, как это должно. Но я предполагаю, что это имеет смысл, учитывая сериализованную форму, в которой отсутствует информация о типе - я полагаю, Джексон просто десериализует ее в карты, поскольку в ней нет фактического ожидаемого типа во время выполнения (спасибо, стирание типа STOOPID!). br /> Реальный вопрос заключается в том, почему Джексон пропускает информацию о типе из сериализованной формы, когда код был запутан, и что я могу с ней сделать? < /p>

Подробнее здесь: https://stackoverflow.com/questions/794 ... phic-types

Вернуться в «JAVA»