Merge pull request #312 from aguibert/property-type-mismatch
Apply adapters based on property read/write type
diff --git a/README.md b/README.md
index b599345..c6d7f2e 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,10 @@
+# Eclipse Yasson
+
+[](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.eclipse%22%20a%3A%22yasson%22)
+[](https://oss.sonatype.org/content/repositories/snapshots/org/eclipse/yasson/)
[](https://travis-ci.org/eclipse-ee4j/yasson)
[](https://opensource.org/licenses/EPL-1.0)
-[](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.eclipse%22%20a%3A%22yasson%22)
-# Yasson
Yasson is a Java framework which provides a standard binding layer between Java classes and JSON documents. This is similar to what JAXB is doing in the XML world. Yasson is an official reference implementation of JSON Binding ([JSR-367](https://jcp.org/en/jsr/detail?id=367)).
It defines a **default mapping** algorithm for converting existing Java classes to JSON suitable for the most cases:
diff --git a/src/main/java/org/eclipse/yasson/internal/ClassParser.java b/src/main/java/org/eclipse/yasson/internal/ClassParser.java
index 832a99f..2edcfc2 100644
--- a/src/main/java/org/eclipse/yasson/internal/ClassParser.java
+++ b/src/main/java/org/eclipse/yasson/internal/ClassParser.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -29,6 +29,7 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
@@ -173,7 +174,7 @@
for (Method method : declaredMethods) {
String name = method.getName();
//isBridge method filters out methods inherited from interfaces
- if (!isPropertyMethod(method) || method.isBridge()) {
+ if (!isPropertyMethod(method) || method.isBridge() || isSpecialCaseMethod(clazz, method)) {
continue;
}
final String propertyName = toPropertyMethod(name);
@@ -181,6 +182,25 @@
Property property = registerMethod(propertyName, method, classElement, classProperties);
}
}
+
+ /**
+ * Filter out certain methods that get forcibly added to some classes.
+ * For example the public groovy.lang.MetaClass X.getMetaClass() method from Groovy classes
+ */
+ private boolean isSpecialCaseMethod(Class<?> clazz, Method m) {
+ if (!Modifier.isPublic(m.getModifiers()) || Modifier.isStatic(m.getModifiers()) || m.isSynthetic())
+ return false;
+ // Groovy objects will have public groovy.lang.MetaClass X.getMetaClass()
+ // which causes an infinite loop in serialization
+ if (m.getName().equals("getMetaClass") &&
+ m.getReturnType().getCanonicalName().equals("groovy.lang.MetaClass"))
+ return true;
+ // WELD proxy objects will have 'public org.jboss.weld
+ if (m.getName().equals("getMetadata") &&
+ m.getReturnType().getCanonicalName().equals("org.jboss.weld.proxy.WeldClientProxy$Metadata"))
+ return true;
+ return false;
+ }
private boolean isGetter(Method m) {
return (m.getName().startsWith(GET_PREFIX) || m.getName().startsWith(IS_PREFIX)) && m.getParameterCount() == 0;
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..95fb57e 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,7 @@
INCOMPATIBLE_FACTORY_CREATOR_RETURN_TYPE("incompatibleFactoryCreatorReturnType"),
MULTIPLE_JSONB_CREATORS("multipleJsonbCreators"),
INTERNAL_ERROR("internalError"),
+ 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..ab81280 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.
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 2443d5d..c8350aa 100644
--- a/src/main/java/org/eclipse/yasson/internal/serializer/ObjectSerializer.java
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/ObjectSerializer.java
@@ -16,7 +16,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;
@@ -55,9 +58,15 @@
@Override
protected void serializeInternal(T object, JsonGenerator generator, SerializationContext ctx) {
- final PropertyModel[] allProperties = ((Marshaller) ctx).getMappingContext().getOrCreateClassModel(object.getClass()).getSortedProperties();
+ 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()), e);
+ }
}
}
diff --git a/src/main/resources/yasson-messages.properties b/src/main/resources/yasson-messages.properties
index 59b8a68..b0efc2b 100644
--- a/src/main/resources/yasson-messages.properties
+++ b/src/main/resources/yasson-messages.properties
@@ -63,6 +63,7 @@
incompatibleFactoryCreatorReturnType=Return type of creator {0} must be of type {1}.
multipleJsonbCreators=More than one @JsonbCreator declared in class {0}.
internalError=Internal error: {0}
+serializePropertyError=Unable to serialize property ''{0}'' from {1}
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..509ba75
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/Assertions.java
@@ -0,0 +1,67 @@
+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 = "";
+ for (Throwable current = t; current != null && current.getCause() != current; current = current.getCause()) {
+ fullErrorMessage += current.getClass().getCanonicalName() + ": ";
+ fullErrorMessage += current.getMessage() + "\n";
+ }
+ 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/customization/JsonbPropertyTest.java b/src/test/java/org/eclipse/yasson/customization/JsonbPropertyTest.java
index e35ed1a..4a350b6 100644
--- a/src/test/java/org/eclipse/yasson/customization/JsonbPropertyTest.java
+++ b/src/test/java/org/eclipse/yasson/customization/JsonbPropertyTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2016 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.
@@ -152,6 +152,22 @@
}
}
+
+ @Test
+ public void testConflictingWithLowercaseStrategy() {
+ // scenario raised by user here: https://github.com/eclipse-ee4j/yasson/issues/296
+ Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES));
+ assertEquals("{\"url\":\"http://foo.com\"}",
+ jsonb.toJson(new ConflictingIfLowercase()));
+ }
+
+ public static class ConflictingIfLowercase {
+ private String url = "foo.com";
+
+ public String getURL() {
+ return "http://" + url;
+ }
+ }
public static class NonConflictingProperties {
private String doi;
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..16d5e5d 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,37 @@
"\"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));
+
+ 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"));
+ }
+
+ @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"));
+ }
}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/ClassModifiersTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/ClassModifiersTest.java
index bf2a7b4..8bb67eb 100644
--- a/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/ClassModifiersTest.java
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/ClassModifiersTest.java
@@ -17,6 +17,7 @@
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbException;
+import org.eclipse.yasson.Assertions;
import org.eclipse.yasson.defaultmapping.modifiers.model.ChildOfPackagePrivateParent;
import org.eclipse.yasson.defaultmapping.modifiers.model.FieldModifiersClass;
import org.junit.Before;
@@ -67,14 +68,9 @@
NestedPackageChild child = new NestedPackageChild();
child.id = 1;
child.name = "SomeName";
- try {
- jsonb.toJson(child);
- fail();
- } catch (JsonbException ex) {
- if (!(ex.getCause() instanceof IllegalAccessException)) {
- fail();
- }
- }
+ Assertions.shouldFail(() -> jsonb.toJson(child),
+ msg -> msg.contains("Unable to serialize property 'id'") &&
+ msg.contains("java.lang.IllegalAccessException"));
}
private class NestedPrivateParent {
@@ -90,14 +86,8 @@
NestedPrivateChild child = new NestedPrivateChild();
child.id = 1;
child.name = "SomeName";
- try {
- jsonb.toJson(child);
- fail();
- } catch (JsonbException ex) {
- if (!(ex.getCause() instanceof IllegalAccessException)) {
- fail();
- }
- }
+ Assertions.shouldFail(() -> jsonb.toJson(child),
+ msg -> msg.contains("java.lang.IllegalAccessException"));
}
@@ -114,14 +104,8 @@
NestedStaticPackageChild child = new NestedStaticPackageChild();
child.id = 1;
child.name = "SomeName";
- try {
- jsonb.toJson(child);
- fail();
- } catch (JsonbException ex) {
- if (!(ex.getCause() instanceof IllegalAccessException)) {
- fail();
- }
- }
+ Assertions.shouldFail(() -> jsonb.toJson(child),
+ msg -> msg.contains("java.lang.IllegalAccessException"));
}
private static class NestedStaticPrivateParent {
@@ -137,14 +121,8 @@
NestedStaticPrivateChild child = new NestedStaticPrivateChild();
child.id = 1;
child.name = "SomeName";
- try {
- jsonb.toJson(child);
- fail();
- } catch (JsonbException ex) {
- if (!(ex.getCause() instanceof IllegalAccessException)) {
- fail();
- }
- }
+ Assertions.shouldFail(() -> jsonb.toJson(child),
+ msg -> msg.contains("java.lang.IllegalAccessException"));
}
}
diff --git a/src/test/java/org/eclipse/yasson/documented/DocumentationExampleTest.java b/src/test/java/org/eclipse/yasson/documented/DocumentationExampleTest.java
new file mode 100644
index 0000000..ac4f751
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/documented/DocumentationExampleTest.java
@@ -0,0 +1,576 @@
+package org.eclipse.yasson.documented;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.json.bind.Jsonb;
+import javax.json.bind.JsonbBuilder;
+import javax.json.bind.JsonbConfig;
+import javax.json.bind.adapter.JsonbAdapter;
+import javax.json.bind.annotation.JsonbCreator;
+import javax.json.bind.annotation.JsonbDateFormat;
+import javax.json.bind.annotation.JsonbNillable;
+import javax.json.bind.annotation.JsonbNumberFormat;
+import javax.json.bind.annotation.JsonbProperty;
+import javax.json.bind.annotation.JsonbTransient;
+import javax.json.bind.serializer.DeserializationContext;
+import javax.json.bind.serializer.JsonbDeserializer;
+import javax.json.bind.serializer.JsonbSerializer;
+import javax.json.bind.serializer.SerializationContext;
+import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonParser;
+
+import org.junit.Test;
+
+/**
+ * Contains tests from http://json-b.net/docs/user-guide.html
+ */
+public class DocumentationExampleTest {
+
+ public static class Dog {
+ public String name;
+ public int age;
+ public boolean bitable;
+ }
+
+ @Test
+ public void testMappingExample() {
+ // Create a dog instance
+ Dog dog = new Dog();
+ dog.name = "Falco";
+ dog.age = 4;
+ dog.bitable = false;
+
+ // Create Jsonb and serialize
+ Jsonb jsonb = JsonbBuilder.create();
+ String result = jsonb.toJson(dog);
+ assertEquals("{\"age\":4,\"bitable\":false,\"name\":\"Falco\"}", result);
+
+ // Deserialize back
+ dog = jsonb.fromJson("{\"name\":\"Falco\",\"age\":4,\"bites\":false}", Dog.class);
+ assertEquals("Falco", dog.name);
+ assertEquals(4, dog.age);
+ assertEquals(false, dog.bitable);
+ }
+
+ @Test
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public void testMappingCollection() {
+ Dog falco = new Dog();
+ falco.name = "Falco";
+ falco.age = 4;
+ Dog cassidy = new Dog();
+ cassidy.name = "Cassidy";
+ cassidy.age = 5;
+
+ // List of dogs
+ List dogs = new ArrayList();
+ dogs.add(falco);
+ dogs.add(cassidy);
+
+ // Create Jsonb and serialize
+ Jsonb jsonb = JsonbBuilder.create();
+ String result = jsonb.toJson(dogs);
+ assertEquals(
+ "[{\"age\":4,\"bitable\":false,\"name\":\"Falco\"},{\"age\":5,\"bitable\":false,\"name\":\"Cassidy\"}]",
+ result);
+
+ // We can also deserialize back into a raw collection, but since there is no way
+ // to infer a type here,
+ // the result will be a list of java.util.Map instances with string keys.
+ dogs = jsonb.fromJson(result, ArrayList.class);
+ assertEquals(2, dogs.size());
+ assertEquals("Falco", ((Map) dogs.get(0)).get("name"));
+ assertEquals("Cassidy", ((Map) dogs.get(1)).get("name"));
+ // assertEquals(4, ((Map) dogs.get(0)).get("age")); // TODO should these
+ // actually be BigDecimals?
+ // assertEquals(5, ((Map) dogs.get(1)).get("age"));
+ }
+
+ @SuppressWarnings("serial")
+ @Test
+ public void testMappingGenericCollection() {
+ Dog falco = new Dog();
+ falco.name = "Falco";
+ falco.age = 4;
+ Dog cassidy = new Dog();
+ cassidy.name = "Cassidy";
+ cassidy.age = 5;
+
+ // List of dogs
+ List<Dog> dogs = new ArrayList<>();
+ dogs.add(falco);
+ dogs.add(cassidy);
+
+ // Create Jsonb and serialize
+ Jsonb jsonb = JsonbBuilder.create();
+ String result = jsonb.toJson(dogs);
+ assertEquals(
+ "[{\"age\":4,\"bitable\":false,\"name\":\"Falco\"},{\"age\":5,\"bitable\":false,\"name\":\"Cassidy\"}]",
+ result);
+
+ // Deserialize back
+ dogs = jsonb.fromJson(result, new ArrayList<Dog>() {
+ }.getClass().getGenericSuperclass());
+ assertEquals(2, dogs.size());
+ assertEquals("Falco", dogs.get(0).name);
+ assertEquals("Cassidy", dogs.get(1).name);
+ }
+
+ @Test
+ public void testFormattedOutput() {
+ Dog pojo = new Dog();
+ pojo.name = "Falco";
+ pojo.age = 4;
+
+ // Create custom configuration with formatted output
+ JsonbConfig config = new JsonbConfig().withFormatting(true);
+
+ // Create Jsonb with custom configuration
+ Jsonb jsonb = JsonbBuilder.create(config);
+
+ // Use it!
+ String result = jsonb.toJson(pojo);
+ assertEquals("\n" +
+ "{\n" +
+ " \"age\": 4,\n" +
+ " \"bitable\": false,\n" +
+ " \"name\": \"Falco\"\n" +
+ "}", result);
+ }
+
+ public static class Person1 {
+ @JsonbProperty("person-name")
+ public String name;
+ public String profession;
+ }
+
+ @Test
+ public void testChangingPropertyNames1() {
+ Person1 p = new Person1();
+ p.name = "Jason Bourne";
+ p.profession = "Super Agent";
+ Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(true));
+ String result = jsonb.toJson(p);
+ assertEquals("\n" +
+ "{\n" +
+ " \"person-name\": \"Jason Bourne\",\n" +
+ " \"profession\": \"Super Agent\"\n" +
+ "}", result);
+ }
+
+ public class Person2 {
+ private String name;
+ private String profession;
+
+ @JsonbProperty("person-name")
+ public String getName() {
+ return name;
+ }
+
+ public String getProfession() {
+ return profession;
+ }
+
+ // public setters ...
+ public void setName(String name) {
+ this.name = name;
+ }
+ public void setProfession(String profession) {
+ this.profession = profession;
+ }
+ }
+
+ @Test
+ public void testChangingPropertyNames2() {
+ Person2 p = new Person2();
+ p.name = "Jason Bourne";
+ p.profession = "Super Agent";
+ Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(true));
+ String result = jsonb.toJson(p);
+ assertEquals("\n" +
+ "{\n" +
+ " \"person-name\": \"Jason Bourne\",\n" +
+ " \"profession\": \"Super Agent\"\n" +
+ "}", result);
+ }
+
+ public static class Person3 {
+ private String name;
+
+ @JsonbProperty("name-to-write")
+ public String getName() {
+ return name;
+ }
+
+ @JsonbProperty("name-to-read")
+ public void setName(String name) {
+ this.name = name;
+ }
+ }
+
+ @Test
+ public void testChangingPropertyNames3() {
+ Person3 p = new Person3();
+ p.name = "Jason Bourne";
+ Jsonb jsonb = JsonbBuilder.create();
+ String result = jsonb.toJson(p);
+ assertEquals("{\"name-to-write\":\"Jason Bourne\"}", result);
+
+ String json = "{\"name-to-read\":\"Jason Bourne\"}";
+ Person3 after = jsonb.fromJson(json, Person3.class);
+ assertEquals("Jason Bourne", after.name);
+ }
+
+ public static class Person4 { // TODO: a non-static class results in an NPE
+ @JsonbTransient
+ private String name;
+
+ private String profession;
+
+ // public getters/setters ...
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getProfession() {
+ return this.profession;
+ }
+ public void setProfession(String profession) {
+ this.profession = profession;
+ }
+ }
+
+ @Test
+ public void testIgnoringProperties() {
+ Person4 p = new Person4();
+ p.name = "Jason Bourne";
+ p.profession = "Super Agent";
+ Jsonb jsonb = JsonbBuilder.create();
+ String result = jsonb.toJson(p);
+ assertEquals("{\"profession\":\"Super Agent\"}", result);
+
+ String json = "{\"profession\":\"Super Agent\"}";
+ Person4 after = jsonb.fromJson(json, Person4.class);
+ assertEquals("Super Agent", after.profession);
+ assertNull(after.name);
+ }
+
+ @JsonbNillable
+ public class Person5 {
+ private String name;
+ private String profession;
+
+ // public getters/setters ...
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getProfession() {
+ return profession;
+ }
+ public void setProfession(String profession) {
+ this.profession = profession;
+ }
+ }
+
+ @Test
+ public void testNullHandling1() {
+ Person5 p = new Person5();
+ Jsonb jsonb = JsonbBuilder.create();
+ String result = jsonb.toJson(p);
+ assertEquals("{\"name\":null,\"profession\":null}", result);
+ }
+
+ public class Person6 {
+ @JsonbProperty(nillable=true)
+ private String name;
+
+ private String profession;
+
+ // public getters/setters ...
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getProfession() {
+ return profession;
+ }
+ public void setProfession(String profession) {
+ this.profession = profession;
+ }
+ }
+
+ @Test
+ public void testNullHandling2() {
+ Person6 p = new Person6();
+ Jsonb jsonb = JsonbBuilder.create();
+ String result = jsonb.toJson(p);
+ assertEquals("{\"name\":null}", result);
+ }
+
+ public static class Person {
+ public String name;
+ public String profession;
+ }
+
+ @Test
+ public void testNullHandling3() {
+ Person p = new Person();
+ Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withNullValues(true));
+ String result = jsonb.toJson(p);
+ assertEquals("{\"name\":null,\"profession\":null}", result);
+ }
+
+ public static class Person8 { // TODO: obscure error here if non-static
+ public final String name;
+ public String profession;
+
+ @JsonbCreator
+ public Person8(@JsonbProperty("name") String name) {
+ this.name = name;
+ }
+ }
+
+ @Test
+ public void testCustomInstantiation() {
+ Jsonb jsonb = JsonbBuilder.create();
+ Person8 p = jsonb.fromJson("{\"name\":\"Jason Bourne\"}", Person8.class);
+ assertEquals("Jason Bourne", p.name);
+ }
+
+ public static class Person9 {
+ public String name;
+
+ @JsonbDateFormat("dd.MM.yyyy")
+ public LocalDate birthDate;
+
+ @JsonbNumberFormat("#0.00")
+ public BigDecimal salary;
+ }
+
+ @Test
+ public void testDateNumberFormats1() {
+ Person9 p = new Person9();
+ p.name = "Jason Bourne";
+ p.birthDate = LocalDate.of(1999, 8, 7);
+ p.salary = new BigDecimal("123.45678");
+ Jsonb jsonb = JsonbBuilder.create();
+ String json = jsonb.toJson(p);
+ assertEquals("{\"birthDate\":\"07.08.1999\",\"name\":\"Jason Bourne\",\"salary\":\"123.46\"}", json);
+
+ Person9 after = jsonb.fromJson("{\"birthDate\":\"07.08.1999\",\"name\":\"Jason Bourne\",\"salary\":\"123.46\"}", Person9.class);
+ assertEquals(p.name, after.name);
+ assertEquals(p.birthDate, after.birthDate);
+ assertEquals(new BigDecimal("123.46"), after.salary);
+ }
+
+ public static class Person10 {
+ public String name;
+
+ public LocalDate birthDate;
+
+ public BigDecimal salary;
+ }
+
+ @Test
+ public void testDateNumberFormats2() {
+ Person10 p = new Person10();
+ p.name = "Jason Bourne";
+ p.birthDate = LocalDate.of(1999, 8, 7);
+ p.salary = new BigDecimal("123.45678");
+ Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()//
+ .withDateFormat("dd.MM.yyyy", null)); // TODO: why no withNumberFormat?
+ String json = jsonb.toJson(p);
+ assertEquals("{\"birthDate\":\"07.08.1999\",\"name\":\"Jason Bourne\",\"salary\":123.45678}", json);
+
+ Person9 after = jsonb.fromJson("{\"birthDate\":\"07.08.1999\",\"name\":\"Jason Bourne\",\"salary\":123.45678}", Person9.class);
+ assertEquals(p.name, after.name);
+ assertEquals(p.birthDate, after.birthDate);
+ assertEquals(p.salary, after.salary);
+ }
+
+ public static class Customer {
+ private int id;
+ private String name;
+ private String organization;
+ private String position;
+
+ public int getId() {
+ return id;
+ }
+ public void setId(int id) {
+ this.id = id;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getOrganization() {
+ return organization;
+ }
+ public void setOrganization(String organization) {
+ this.organization = organization;
+ }
+ public String getPosition() {
+ return position;
+ }
+ public void setPosition(String position) {
+ this.position = position;
+ }
+ }
+
+ public static class CustomerAnnotated {
+ @JsonbProperty("customer_id")
+ private int id;
+
+ @JsonbProperty("customer_name")
+ private String name;
+
+ public int getId() {
+ return id;
+ }
+ public void setId(int id) {
+ this.id = id;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ }
+
+ public static class CustomerAdapter implements JsonbAdapter<Customer, CustomerAnnotated> {
+ @Override
+ public CustomerAnnotated adaptToJson(Customer c) throws Exception {
+ CustomerAnnotated customer = new CustomerAnnotated();
+ customer.setId(c.getId());
+ customer.setName(c.getName());
+ return customer;
+ }
+
+ @Override
+ public Customer adaptFromJson(CustomerAnnotated adapted) throws Exception {
+ Customer customer = new Customer();
+ customer.setId(adapted.getId());
+ customer.setName(adapted.getName());
+ return customer;
+ }
+ }
+
+ @Test
+ public void testAdapters1() {
+ // Create Jsonb with default configuration
+ Jsonb jsonb = JsonbBuilder.create();
+
+ // Create customer
+ Customer customer = new Customer();
+
+ customer.setId(1);
+ customer.setName("Jason Bourne");
+ customer.setOrganization("Super Agents");
+ customer.setPosition("Super Agent");
+
+ // Serialize
+ String json = jsonb.toJson(customer);
+ assertEquals("{\"id\":1,\"name\":\"Jason Bourne\",\"organization\":\"Super Agents\",\"position\":\"Super Agent\"}", json);
+ }
+
+ @Test
+ public void testAdapters2() {
+ // Create custom configuration
+ JsonbConfig config = new JsonbConfig()
+ .withAdapters(new CustomerAdapter());
+
+ // Create Jsonb with custom configuration
+ Jsonb jsonb = JsonbBuilder.create(config);
+
+ // Create customer
+ Customer customer = new Customer();
+
+ customer.setId(1);
+ customer.setName("Jason Bourne");
+ customer.setOrganization("Super Agents");
+ customer.setPosition("Super Agent");
+
+ // Serialize
+ String json = jsonb.toJson(customer);
+ assertEquals("{\"customer_id\":1,\"customer_name\":\"Jason Bourne\"}", json);
+ }
+
+ public static class CustomerSerializer implements JsonbSerializer<Customer> {
+ @Override
+ public void serialize(Customer customer, JsonGenerator generator, SerializationContext ctx) {
+ generator.writeStartObject();
+ generator.write("customer_id", customer.getId());
+ generator.write("customer_name", customer.getName());
+ generator.writeEnd();
+ }
+ }
+
+ public static class CustomerDeserializer implements JsonbDeserializer<Customer> {
+ @Override
+ public Customer deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
+ Customer customer = new Customer();
+ JsonParser.Event next;
+
+ // Moving parser by hand looking for customer_id and customer_name properties
+ while ((next = parser.next()) != JsonParser.Event.END_OBJECT) {
+ if (next == JsonParser.Event.KEY_NAME) {
+ String jsonKeyName = parser.getString();
+
+ // Move to json value
+ parser.next();
+
+ if ("customer_id".equals(jsonKeyName)) {
+ customer.setId(parser.getInt());
+ } else if ("customer_name".equals(jsonKeyName)) {
+ customer.setName(parser.getString());
+ }
+ }
+ }
+ return customer;
+ }
+ }
+
+ @Test
+ public void testSerializerDeserializer() {
+ // Create pojo
+ Customer customer = new Customer();
+ customer.setId(1);
+ customer.setName("Freddie");
+ customer.setOrganization("Super Agents");
+ customer.setPosition("Super Agent");
+
+ // Also configurable with @JsonbSerializer / JsonbDeserializer on properties and class.
+ JsonbConfig config = new JsonbConfig()
+ .withSerializers(new CustomerSerializer())
+ .withDeserializers(new CustomerDeserializer());
+
+ Jsonb jsonb = JsonbBuilder.create(config);
+ String json = jsonb.toJson(customer);
+ assertEquals("{\"customer_id\":1,\"customer_name\":\"Freddie\"}", json);
+
+ Customer result = jsonb.fromJson(json, Customer.class);
+ assertEquals(customer.getId(), result.getId());
+ assertEquals(customer.getName(), result.getName());
+ assertNull(result.getOrganization());
+ assertNull(result.getPosition());
+ }
+}
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