Include property name/value when serialization fails
diff --git a/src/main/java/org/eclipse/yasson/internal/properties/MessageKeys.java b/src/main/java/org/eclipse/yasson/internal/properties/MessageKeys.java index 3336dda..fdd3793 100644 --- a/src/main/java/org/eclipse/yasson/internal/properties/MessageKeys.java +++ b/src/main/java/org/eclipse/yasson/internal/properties/MessageKeys.java
@@ -68,6 +68,8 @@ INCOMPATIBLE_FACTORY_CREATOR_RETURN_TYPE("incompatibleFactoryCreatorReturnType"), MULTIPLE_JSONB_CREATORS("multipleJsonbCreators"), INTERNAL_ERROR("internalError"), + SERIALIZE_VALUE_ERROR("serializeValueError"), + SERIALIZE_PROPERTY_ERROR("serializePropertyError"), DESERIALIZE_VALUE_ERROR("deserializeValueError"), PARSING_NUMBER("parsingNumber"), UNKNOWN_BINARY_DATA_STRATEGY("unknownBinaryDataStrategy"),
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/AbstractValueTypeSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/AbstractValueTypeSerializer.java index 242a3e9..721a1a6 100644 --- a/src/main/java/org/eclipse/yasson/internal/serializer/AbstractValueTypeSerializer.java +++ b/src/main/java/org/eclipse/yasson/internal/serializer/AbstractValueTypeSerializer.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. @@ -15,7 +15,10 @@ import org.eclipse.yasson.internal.Marshaller; import org.eclipse.yasson.internal.model.customization.Customization; +import org.eclipse.yasson.internal.properties.MessageKeys; +import org.eclipse.yasson.internal.properties.Messages; +import javax.json.bind.JsonbException; import javax.json.bind.serializer.JsonbSerializer; import javax.json.bind.serializer.SerializationContext; import javax.json.stream.JsonGenerator; @@ -48,7 +51,12 @@ @Override public void serialize(T obj, JsonGenerator generator, SerializationContext ctx) { Marshaller marshaller = (Marshaller) ctx; - serialize(obj, generator, marshaller); + try { + serialize(obj, generator, marshaller); + } catch (Exception e) { + throw new JsonbException(Messages.getMessage(MessageKeys.SERIALIZE_VALUE_ERROR, + obj, obj.getClass().getCanonicalName(), e.getMessage())); + } } protected abstract void serialize(T obj, JsonGenerator generator, Marshaller marshaller);
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/ObjectSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/ObjectSerializer.java index 0194151..f791645 100644 --- a/src/main/java/org/eclipse/yasson/internal/serializer/ObjectSerializer.java +++ b/src/main/java/org/eclipse/yasson/internal/serializer/ObjectSerializer.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. @@ -17,7 +17,10 @@ import org.eclipse.yasson.internal.ReflectionUtils; import org.eclipse.yasson.internal.model.ClassModel; import org.eclipse.yasson.internal.model.PropertyModel; +import org.eclipse.yasson.internal.properties.MessageKeys; +import org.eclipse.yasson.internal.properties.Messages; +import javax.json.bind.JsonbException; import javax.json.bind.serializer.JsonbSerializer; import javax.json.bind.serializer.SerializationContext; import javax.json.stream.JsonGenerator; @@ -58,7 +61,12 @@ protected void serializeInternal(T object, JsonGenerator generator, SerializationContext ctx) { final PropertyModel[] allProperties = ((Marshaller) ctx).getMappingContext().getOrCreateClassModel(object.getClass()).getSortedProperties(); for (PropertyModel model : allProperties) { - marshallProperty(object, generator, ctx, model); + try { + marshallProperty(object, generator, ctx, model); + } catch (Exception e) { + throw new JsonbException(Messages.getMessage(MessageKeys.SERIALIZE_PROPERTY_ERROR, + model.getWriteName(), object.getClass().getCanonicalName(), model.getValue(object)), e); + } } } @@ -72,7 +80,6 @@ generator.writeStartObject(key); } - @SuppressWarnings("unchecked") private void marshallProperty(T object, JsonGenerator generator, SerializationContext ctx, PropertyModel propertyModel) { Marshaller marshaller = (Marshaller) ctx;
diff --git a/src/main/resources/yasson-messages.properties b/src/main/resources/yasson-messages.properties index 59b8a68..55c2b78 100644 --- a/src/main/resources/yasson-messages.properties +++ b/src/main/resources/yasson-messages.properties
@@ -63,6 +63,8 @@ incompatibleFactoryCreatorReturnType=Return type of creator {0} must be of type {1}. multipleJsonbCreators=More than one @JsonbCreator declared in class {0}. internalError=Internal error: {0} +serializeValueError=Unable to serialize value ''{0}'' of type {1} because of: {2} +serializePropertyError=Unable to serialize property ''{0}'' from {1} with value ''{2}'' deserializeValueError=Error deserialize JSON value into type: {0}. parsingNumber=Error parsing number {0} with format {1}. unknownBinaryDataStrategy=Unknown binary data strategy: {0}
diff --git a/src/test/java/org/eclipse/yasson/Assertions.java b/src/test/java/org/eclipse/yasson/Assertions.java new file mode 100644 index 0000000..c1b19e2 --- /dev/null +++ b/src/test/java/org/eclipse/yasson/Assertions.java
@@ -0,0 +1,66 @@ +package org.eclipse.yasson; + +import static org.junit.Assert.fail; + +import java.util.function.Function; +import java.util.function.Supplier; + +import javax.json.bind.JsonbException; + +public class Assertions { + + /** + * Asserts that the given operation will fail with a JsonbException + * @param operation The operation that is expected to fail + */ + public static void shouldFail(Supplier<?> operation) { + shouldFail(operation, JsonbException.class, msg -> true); + } + + public static void shouldFail(Runnable operation) { + shouldFail(() -> { + operation.run(); + return null; + }); + } + + /** + * Asserts that the given operation will fail with a JsonbException + * @param operation The operation that is expected to fail + * @param checkExceptionMessage Any checks that should be made on the exception message. For example, ensuring the exception + * includes a specific token. + */ + public static void shouldFail(Supplier<?> operation, Function<String,Boolean> checkExceptionMessage) { + shouldFail(operation, JsonbException.class, checkExceptionMessage); + } + + /** + * Asserts that the given operation will fail + * @param operation The operation that is expected to fail + * @param expectedType The expected exception type to receive when evaluating the operation + * @param checkExceptionMessage Any checks that should be made on the exception message. For example, ensuring the exception + * includes a specific token. + */ + public static void shouldFail(Supplier<?> operation, Class<? extends Throwable> expectedType, Function<String,Boolean> checkExceptionMessage) { + try { + operation.get(); + fail("The operation should have failed with a " + expectedType.getCanonicalName() + " but it succeeded."); + } catch (Throwable t) { + String fullErrorMessage = t.getMessage(); + for (Throwable current = t; current.getCause() != null && current.getCause() != current; current = current.getCause()) { + fullErrorMessage += current.getMessage(); + } + if (expectedType.isAssignableFrom(t.getClass())) { + if (!checkExceptionMessage.apply(fullErrorMessage)) { + t.printStackTrace(); + fail("Exception did not contain the proper content: " + fullErrorMessage); + } + } else { + t.printStackTrace(); + fail("Expected to get an exception of " + expectedType + " but instead was " + t.getClass()); + } + } + + } + +}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/basic/NumberTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/basic/NumberTest.java index a2cbadc..8dfa547 100644 --- a/src/test/java/org/eclipse/yasson/defaultmapping/basic/NumberTest.java +++ b/src/test/java/org/eclipse/yasson/defaultmapping/basic/NumberTest.java
@@ -14,6 +14,7 @@ package org.eclipse.yasson.defaultmapping.basic; +import static org.eclipse.yasson.Assertions.*; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.defaultmapping.basic.model.BigDecimalInNumber; import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper; @@ -26,10 +27,18 @@ import javax.json.JsonWriter; import javax.json.bind.Jsonb; import javax.json.bind.JsonbBuilder; +import javax.json.bind.JsonbException; import javax.json.stream.JsonGenerator; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.io.StringWriter; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; /** * @author Roman Grigoriadi @@ -207,5 +216,39 @@ "\"unsafeBigDecimalValue\":9223372036854775807}", w.toString()); } + + public static class NumberContainer { + public Double doubleProp; + public Collection<Double> collectionProp; + public Map<String,Double> mapProp; + } + + @Test + public void testSerializeInvalidDouble() { + shouldFail(() -> jsonb.toJson(Double.POSITIVE_INFINITY), + msg -> msg.contains("Unable to serialize value") && msg.contains("java.lang.Double")); + + NumberContainer obj = new NumberContainer(); + obj.doubleProp = Double.POSITIVE_INFINITY; + shouldFail(() -> jsonb.toJson(obj), + msg -> msg.contains("doubleProp") && msg.contains("NumberContainer")); + } + + + @Test + public void testSerializeInvalidDoubleCollection() { + NumberContainer obj = new NumberContainer(); + obj.collectionProp = Collections.singleton(Double.POSITIVE_INFINITY); + shouldFail(() -> jsonb.toJson(obj), + msg -> msg.contains("collectionProp") && msg.contains("NumberContainer") && msg.contains("Infinity")); + } + + @Test + public void testSerializeInvalidDoubleMap() { + NumberContainer obj = new NumberContainer(); + obj.mapProp = Collections.singletonMap("doubleKey", Double.POSITIVE_INFINITY); + shouldFail(() -> jsonb.toJson(obj), + msg -> msg.contains("mapProp") && msg.contains("NumberContainer") && msg.contains("Infinity")); + } }
diff --git a/src/test/java/org/eclipse/yasson/jsonpsubstitution/PreinstantiatedJsonpTest.java b/src/test/java/org/eclipse/yasson/jsonpsubstitution/PreinstantiatedJsonpTest.java index 63337b0..d72b799 100644 --- a/src/test/java/org/eclipse/yasson/jsonpsubstitution/PreinstantiatedJsonpTest.java +++ b/src/test/java/org/eclipse/yasson/jsonpsubstitution/PreinstantiatedJsonpTest.java
@@ -9,6 +9,7 @@ ******************************************************************************/ package org.eclipse.yasson.jsonpsubstitution; +import org.eclipse.yasson.Assertions; import org.eclipse.yasson.JsonBindingProvider; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.YassonJsonb; @@ -165,12 +166,7 @@ generator.writeStartObject(); //key not written - try { - jsonb.toJson(dog, generator); - Assert.fail("JsonbException not thrown"); - } catch (JsonbException e) { - //OK - } + Assertions.shouldFail(() -> jsonb.toJson(dog, generator)); } @Test