Open nimo23 opened 2 years ago
Could you provide a reproducer please?.
@jbescos Here is the test case leading to this exception:
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
import javax.json.bind.config.PropertyVisibilityStrategy;
import org.junit.jupiter.api.Test;
class BigDecimalJsonTest {
public static class NumberText {
private final BigDecimal number;
private final MathContext mathContext;
public NumberText(BigDecimal number, MathContext mathContext) {
this.number = number;
this.mathContext = mathContext;
}
public BigDecimal getNumber() {
return number;
}
public MathContext getMathContext() {
return mathContext;
}
}
public static final class PrivateVisibilityStrategy implements PropertyVisibilityStrategy {
@Override
public boolean isVisible(Field field) {
return true;
}
@Override
public boolean isVisible(Method method) {
return method.isAnnotationPresent(javax.json.bind.annotation.JsonbProperty.class);
}
}
@Test
final void testNumberText() {
var mathContext = new MathContext(32, RoundingMode.HALF_UP);
var numberText = new NumberText(BigDecimal.valueOf(5.3), mathContext);
var json = toJson(numberText);
System.out.println(json);
assertNotNull(json);
}
public static String toJson(Object object) {
try (var jsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(true)
.withNullValues(true)
// using "PrivateVisibilityStrategy" leads to an exception
.withPropertyVisibilityStrategy(new PrivateVisibilityStrategy()))) {
return jsonb.toJson(object);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
I found the reason for this exception:
I am using the PrivateVisibilityStrategy
which leads to this exception. However, it's not very convenient to turn this off because in my case NumberText
is just a property of another class hierarchy where I need the PrivateVisibilityStrategy
. So if I disable it just because of MathContext
, then the PrivateVisibilityStrategy
for all other properties within the class hierarchy will also be disabled.
I think the issue is that java.math module is not opened to java.base module, but you can explicitly open it with this JVM argument:
--add-opens java.base/java.math=ALL-UNNAMED
MathContext
is not supported by Yasson currently. If you need this to be serialized/deserialized properly, please create your own custom Serializer/Deserializer for it.
@Verdent Look here with the current version:
Caused by: jakarta.json.bind.JsonbException: Unable to serialize property 'amount' from user.Item
at deployment.test.war/org.eclipse.yasson.internal.serializer.ObjectSerializer.lambda$serialize$0(ObjectSerializer.java:43)
at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721)
at deployment.test.war/org.eclipse.yasson.internal.serializer.ObjectSerializer.serialize(ObjectSerializer.java:38)
at deployment.test.war/org.eclipse.yasson.internal.serializer.RecursionChecker.serialize(RecursionChecker.java:38)
at deployment.test.war/org.eclipse.yasson.internal.serializer.KeyWriter.serialize(KeyWriter.java:41)
at deployment.test.war/org.eclipse.yasson.internal.serializer.NullVisibilitySwitcher.serialize(NullVisibilitySwitcher.java:40)
at deployment.test.war/org.eclipse.yasson.internal.serializer.NullSerializer.serialize(NullSerializer.java:67)
at deployment.test.war/org.eclipse.yasson.internal.serializer.types.ObjectTypeSerializer.findSerializer(ObjectTypeSerializer.java:68)
at deployment.test.war/org.eclipse.yasson.internal.serializer.types.ObjectTypeSerializer.serializeValue(ObjectTypeSerializer.java:50)
at deployment.test.war/org.eclipse.yasson.internal.serializer.types.TypeSerializer$ValueSerializer.serialize(TypeSerializer.java:51)
at deployment.test.war/org.eclipse.yasson.internal.serializer.types.TypeSerializer.serialize(TypeSerializer.java:37)
at deployment.test.war/org.eclipse.yasson.internal.serializer.types.ObjectTypeSerializer.serialize(ObjectTypeSerializer.java:31)
at deployment.test.war/org.eclipse.yasson.internal.serializer.NullSerializer.serialize(NullSerializer.java:67)
at deployment.test.war/org.eclipse.yasson.internal.serializer.CollectionSerializer.lambda$serialize$0(CollectionSerializer.java:37)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at deployment.test.war/org.eclipse.yasson.internal.serializer.CollectionSerializer.serialize(CollectionSerializer.java:37)
at deployment.test.war/org.eclipse.yasson.internal.serializer.KeyWriter.serialize(KeyWriter.java:41)
at deployment.test.war/org.eclipse.yasson.internal.serializer.NullVisibilitySwitcher.serialize(NullVisibilitySwitcher.java:40)
at deployment.test.war/org.eclipse.yasson.internal.serializer.NullSerializer.serialize(NullSerializer.java:67)
at deployment.test.war/org.eclipse.yasson.internal.serializer.ValueGetterSerializer.serialize(ValueGetterSerializer.java:43)
at deployment.test.war/org.eclipse.yasson.internal.serializer.ObjectSerializer.lambda$serialize$0(ObjectSerializer.java:41)
... 104 more
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field final java.math.RoundingMode java.math.MathContext.roundingMode accessible: module java.base does not "opens java.math" to unnamed module @765877f0
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:180)
at java.base/java.lang.reflect.Field.setAccessible(Field.java:174)
at deployment.test.war/org.eclipse.yasson.internal.model.PropertyModel.lambda$overrideAccessible$2(PropertyModel.java:594)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:318)
at deployment.test.war/org.eclipse.yasson.internal.model.PropertyModel.overrideAccessible(PropertyModel.java:593)
at deployment.test.war/org.eclipse.yasson.internal.model.PropertyModel.isFieldVisible(PropertyModel.java:568)
at deployment.test.war/org.eclipse.yasson.internal.model.PropertyModel.createReadHandle(PropertyModel.java:516)
at deployment.test.war/org.eclipse.yasson.internal.model.PropertyModel.<init>(PropertyModel.java:157)
at deployment.test.war/org.eclipse.yasson.internal.ClassParser.lambda$parseProperties$0(ClassParser.java:70)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.HashMap$ValueSpliterator.forEachRemaining(HashMap.java:1779)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
at deployment.test.war/org.eclipse.yasson.internal.ClassParser.parseProperties(ClassParser.java:71)
at deployment.test.war/org.eclipse.yasson.internal.MappingContext.lambda$createParseClassModelFunction$1(MappingContext.java:105)
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
at deployment.test.war/org.eclipse.yasson.internal.MappingContext.getOrCreateClassModel(MappingContext.java:77)
at deployment.test.war/org.eclipse.yasson.internal.serializer.SerializationModelCreator.serializerChainInternal(SerializationModelCreator.java:187)
at deployment.test.war/org.eclipse.yasson.internal.serializer.SerializationModelCreator.serializerChain(SerializationModelCreator.java:137)
at deployment.test.war/org.eclipse.yasson.internal.serializer.SerializationModelCreator.memberSerializer(SerializationModelCreator.java:381)
at deployment.test.war/org.eclipse.yasson.internal.serializer.SerializationModelCreator.createObjectSerializer(SerializationModelCreator.java:213)
at deployment.test.war/org.eclipse.yasson.internal.serializer.SerializationModelCreator.serializerChainInternal(SerializationModelCreator.java:199)
at deployment.test.war/org.eclipse.yasson.internal.serializer.SerializationModelCreator.serializerChainRuntime(SerializationModelCreator.java:123)
at deployment.test.war/org.eclipse.yasson.internal.serializer.types.ObjectTypeSerializer.lambda$findSerializer$0(ObjectTypeSerializer.java:67)
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
at deployment.test.war/org.eclipse.yasson.internal.serializer.types.ObjectTypeSerializer.findSerializer(ObjectTypeSerializer.java:65)
at deployment.test.war/org.eclipse.yasson.internal.serializer.types.ObjectTypeSerializer.serializeValue(ObjectTypeSerializer.java:50)
at deployment.test.war/org.eclipse.yasson.internal.serializer.types.TypeSerializer$ValueSerializer.serialize(TypeSerializer.java:51)
at deployment.test.war/org.eclipse.yasson.internal.serializer.types.TypeSerializer.serialize(TypeSerializer.java:37)
at deployment.test.war/org.eclipse.yasson.internal.serializer.types.ObjectTypeSerializer.serialize(ObjectTypeSerializer.java:31)
at deployment.test.war/org.eclipse.yasson.internal.serializer.NullSerializer.serialize(NullSerializer.java:67)
at deployment.test.war/org.eclipse.yasson.internal.serializer.ValueGetterSerializer.serialize(ValueGetterSerializer.java:43)
at deployment.test.war/org.eclipse.yasson.internal.serializer.ObjectSerializer.lambda$serialize$0(ObjectSerializer.java:41)
... 124 more
MathContext is not supported by Yasson currently.
Why? It has only two properties final int precision;
and final RoundingMode roundingMode;
(or their getters getPrecision()
, getRoundingMode()
) which can be easily (de)serialized by yasson. I think I can remember that this was already possible in one of the earlier versions of Yasson, because I used that lib back then and didn't have these problems with exactly this class. But I don't remember which version was suddenly no longer possible. I think, in combination with (de)serializing BigDecimal
, de)serializing MathContext
as well has it needs..
What must be done to support it?
I can provide a PR. I wrote a MathContextAdapter
:
public class MathContextAdapter implements JsonbAdapter<MathContext, JsonObject> {
@Override
public JsonObject adaptToJson(MathContext mathContext) throws Exception {
return Json.createObjectBuilder()
.add("precision", mathContext.getPrecision())
.add("roundingMode", mathContext.getRoundingMode().name())
.build();
}
@Override
public MathContext adaptFromJson(JsonObject jsonObject) throws Exception {
var precision = jsonObject.getInt("precision");
var roundingMode = jsonObject.getString("roundingMode");
MathContext mathContext = new MathContext(precision, RoundingMode.valueOf(roundingMode));
return mathContext;
}
}
However, looking into e.g. https://github.com/eclipse-ee4j/yasson/tree/master/src/main/java/org/eclipse/yasson/internal, Yasson only accepts serializers and deserializers. Should I then implement MathContextSerializer
/MathContextDeserializer
?
I think it would be good to support MathContext
by default (so the user does not need to write a adapter) since it is a Java built-in class and like other classes (like ZoneId
, which is also supported by Yasson) it is a common Java class.
Another question: Why do I even have to register such a (De)serializer to be able to (de)serialize this class? Normally Json-B can (de)serialize a common class like this (consisting only of a String
and an Enum
property) by default.
Describe the bug I get this error when trying to create json output of an instance of
java.math.MathContext
.To Reproduce Steps to reproduce the bug
Expected behavior To be able to (de)serialize
java.math.MathContext
.System information: