Open
Description
Below code works with Jackson 2.8 version but with jackson 2.12x (Jackson-dataformat-ion), deserialization test case fails.
Also tried with intializing ionObject mapper by disabling native type ids but it didnt work.
IonObjectMapper m = new IonObjectMapper().disable(IonGenerator.Feature.USE_NATIVE_TYPE_ID);
Test case results are :
Expected :is <ChildSelector(value=FareCard(elementFactor=FARE, fares=[FARE(type=MARGIN, value=5.00 USD, level=SINGLE)]))>
Actual :<ChildSelector(value=null)>
--- TestClass Code ---
import com.amazon.ion.IonValue;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.dataformat.ion.EnumAsIonSymbolSerializer;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import com.fasterxml.jackson.core.type.TypeReference;
import javax.annotation.Nonnull;
import java.beans.ConstructorProperties;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Collections;
public class Test {
private static final String TEST_SELECTOR_STR = "Child::{" +
"element_factor: FARE," +
"fares: [{ type: MARGIN, level: SINGLE, value: { amount: \"10.00\", currencyCode: \"USD\" } }]}";
private static final IonValue TEST_SELECTOR_ION = IonTestUtils.deserializeIon(TEST_SELECTOR_STR);
private static final ChildSelector<FareCard> TEST_SELECTOR = new ChildSelector<>(TEST_FARE_CARD);
private static final FareCard TEST_FARE_CARD = new FareCard(Element.FARE, Collections.singletonList(
Fare.marginFare(new Currency("10.00", "USD"))));
@Test
public void testDeserialize() throws Exception {
Rule<FareCard> rc = NoSQLIonValue.deserialize(TEST_SELECTOR_ION, new TypeReference<ChildSelector<FareCard>>() {});
assertThat(rc, is(TEST_SELECTOR));
}
@Test
public void testSerialize() throws Exception {
IonValue ion = NoSQLIonValue.serialize(TEST_SELECTOR);
assertThat(ion, is(TEST_SELECTOR_ION));
}
}
---NoSQLIonValue.java---
package com.jacksonissue;
import com.amazon.ion.IonList;
import com.amazon.ion.IonValue;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.dataformat.ion.IonGenerator;
import com.fasterxml.jackson.dataformat.ion.IonObjectMapper;
import com.fasterxml.jackson.dataformat.ion.ionvalue.IonValueModule;
import com.jacksonissue.NoSQLValue;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.core.JsonGenerator;
import com.netbeetle.jackson.ConstructorPropertiesAnnotationIntrospector;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class NoSQLIonValue {
public static <T> T deserialize(@Nonnull IonValue ionValue, TypeReference tr) {
return fMapper.readValue(ionValue, tr);
}
public static <T extends NoSQLValue> IonValue serialize(@Nonnull T obj) {
return fMapper.writeValueAsIonValue(obj);
}
private static IonObjectMapper fMapper = new IonObjectMapper();
static {
fMapper.setPropertyNamingStrategy(new NoSQLPropertyNamingStrategy());
// Lombok sticks @ConstructorProperties on generated constructors - this lets Jackson read it
fMapper.setConfig(fMapper.getDeserializationConfig().withAppendedAnnotationIntrospector(
new ConstructorPropertiesAnnotationIntrospector()));
fMapper.registerModule(new IonValueModule());
fMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
fMapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
fMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
}
---Rule.java---
package com.jacksonissue;
import com.fasterxml.jackson.dataformat.ion.polymorphism.IonAnnotationTypeResolverBuilder;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonTypeResolver;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
@JsonTypeResolver(IonAnnotationTypeResolverBuilder.class)
@JsonSubTypes({
@JsonSubTypes.Type(AnotherSelector.class),
@JsonSubTypes.Type(ChildSelector.class)
})
public interface Rule<T> {
}
--- ChildSelector.java ---
package com.jacksonissue;
import com.jacksonissue.nosqlvalue.NoSQLValue;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import java.io.Serializable;
@Data
@EqualsAndHashCode(callSuper = false)
@JsonTypeName("Child")
public class ChildSelector<T> extends NoSQLValue implements Rule<T>, Serializable {
private static final long serialVersionUID = 1L;
@Getter(onMethod = @__({@JsonValue}))
private final T value;
@Override
public boolean isValid() {
return true;
}
}
---FareCard.java---
package com.jacksonissue;
import com.fasterxml.jackson.dataformat.ion.EnumAsIonSymbolSerializer;
import com.jacksonissue.NoSQLValue;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import lombok.AccessLevel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.Value;
import lombok.experimental.NonFinal;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Data
@NoArgsConstructor(access = AccessLevel.PUBLIC)
@EqualsAndHashCode(callSuper = false)
public class FareCard extends NoSQLValue implements Serializable {
private static final long serialVersionUID = 1L;
@JsonSerialize(using = EnumAsIonSymbolSerializer.class)
private Element elementFactor;
private List<Fare> fares;
public FareCard(Element elementFactor, List<Fare> fares) {
this.elementFactor = elementFactor;
this.fares = fares;
}
}
-- FARE.java --
package com.jacksonissue;
import com.jacksonissue.Currency;
import com.fasterxml.jackson.dataformat.ion.EnumAsIonSymbolSerializer;
import com.jacksonissue.NoSQLValue;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import java.beans.ConstructorProperties;
import java.io.Serializable;
import java.math.BigDecimal;
import static com.google.common.base.Preconditions.checkArgument;
@Data
@EqualsAndHashCode(callSuper = false)
@JsonInclude(JsonInclude.Include.NON_NULL)
@Log4j2
public class Fare extends com.jacksonissue.nosqlvalue.NoSQLValue implements Serializable {
@RequiredArgsConstructor
public enum Type {
BASEMARGIN(Level.MULTIPLE),
MARGIN(Level.SINGLE);
}
@JsonSerialize(using = EnumAsIonSymbolSerializer.class)
@NonNull
private Fare.Type type;
@NonNull
private Currency value;
@JsonSerialize(using = EnumAsIonSymbolSerializer.class)
private Level level;
@ConstructorProperties({"type", "value", "level"})
public Fare(@NonNull Fare.Type type, @NonNull Currency value, @NonNull Level level) {
this.type = type;
this.value = value;
this.level = level;
}
public static Fare marginFare(@NonNull Currency value) {
return new Fare(Fare.Type.MARGIN, value, Level.SINGLE);
}
}
-- Currency.java --
package com.jacksonissue;
import java.io.Serializable;
import java.math.BigDecimal;
public final class Currency implements Comparable, Serializable {
private static final long serialVersionUID = -351418430273563584L;
private static final BigDecimal ZERO = new BigDecimal("0");
private final BigDecimal amount_;
private final String currencyCode;
public Currency(BigDecimal amount, String currencyCode) {
this.amount_ = amount;
this.currencyCode = currencyCode;
}
public Currency(String amount, String currencyCode) {
this(amount == null ? null : new BigDecimal(amount), currencyCode);
}
}
---Level.java ---
package com.jacksonissue;
public enum Level {
MULTIPLE, SINGLE;
}
--Element.java--
package com.jacksonissue;
public enum Element {
FARE,
SUM;
}
--NoSQLValue.java--
package com.jacksonissue;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class NoSQLValue {
@JsonIgnore
public abstract boolean isValid();
@Override
public String toString() {
return ReflectionToStringBuilder.toString(this);
}
}
-- IonTestUtils.java --
package com.jacksonissue;
import com.amazon.ion.IonSystem;
import com.amazon.ion.IonValue;
import com.amazon.ion.system.IonSystemBuilder;
import lombok.experimental.UtilityClass;
@UtilityClass
public final class IonTestUtils {
private static IonSystem ionSystem;
public static synchronized IonSystem getIonSystem() {
if (ionSystem == null) {
ionSystem = IonSystemBuilder.standard().build();
}
return ionSystem;
}
@SuppressWarnings("unchecked")
public static <T extends IonValue> T deserializeIon(final String ionText) {
return (T) getIonSystem().singleValue(ionText);
}
}
--sym--
$ion_shared_symbol_table::{
name:"sym",
version:1,
symbols:[
"Child",
"element_factor",
"FARE",
"level",
"SINGLE",
"MULTIPLE",
"SUM",
"amount",
"currencyCode",
"value"
],
}