diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/Calls.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/Calls.java new file mode 100644 index 00000000..83d1b88f --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/Calls.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry; + +import java.util.ArrayList; +import java.util.List; + +public class Calls { + + private final List calls = new ArrayList<>(); + + public String called() { + final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + + if (stackTrace.length > 2) { + final StackTraceElement caller = stackTrace[2]; + final String className = caller.getClassName(); + final String methodName = caller.getMethodName(); + + final int lastDot = className.lastIndexOf('.'); + final int lastDollar = className.lastIndexOf('$'); + final String simpleName; + if (lastDollar == 0 && lastDot == 0) { + simpleName = className; + } else { + final int start = Math.max(lastDollar, lastDot) + 1; + simpleName = className.substring(start); + } + + final String result = simpleName + "." + methodName; + calls.add(result); + return result; + } + return null; + } + + public String called(final Object instance) { + final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + + if (stackTrace.length > 2) { + final StackTraceElement caller = stackTrace[2]; + final String simpleName = instance.getClass().getSimpleName(); + final String methodName = caller.getMethodName(); + + + final String result = simpleName + "." + methodName; + calls.add(result); + return result; + } + return null; + } + + public List list() { + return new ArrayList<>(calls); + } + + public String get() { + final String result = String.join("\n", calls); + calls.clear(); + return result; + } + + public void reset() { + calls.clear(); + } +} \ No newline at end of file diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/SymmetryTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/SymmetryTest.java new file mode 100644 index 00000000..2bf7a3b7 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/SymmetryTest.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry; + +import jakarta.json.bind.Jsonb; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public abstract class SymmetryTest { + + public abstract Jsonb jsonb(); + + public abstract void assertWrite(final Jsonb jsonb); + + public abstract void assertRead(final Jsonb jsonb); + + /** + * Assert a simple write operation + */ + @Test + public void write() throws Exception { + try (final Jsonb jsonb = jsonb()) { + assertWrite(jsonb); + } + } + + /** + * Assert a simple read operation + */ + @Test + public void read() throws Exception { + try (final Jsonb jsonb = jsonb()) { + assertRead(jsonb); + } + } + + /** + * Validate any caching done from a write operation + * leads to a consistent result on any future + * write operations + */ + @Test + public void writeAfterWrite() throws Exception { + try (final Jsonb jsonb = jsonb()) { + assertWrite(jsonb); + assertWrite(jsonb); + assertWrite(jsonb); + assertWrite(jsonb); + } + } + + /** + * Validate any caching done from a read operation + * leads to a consistent result on any future + * read operations + */ + @Test + public void readAfterRead() throws Exception { + try (final Jsonb jsonb = jsonb()) { + assertRead(jsonb); + assertRead(jsonb); + assertRead(jsonb); + assertRead(jsonb); + } + } + + /** + * Validate any caching done from a read operation + * does not alter the expected behavior of a write + * operation + */ + @Test + public void writeAfterRead() throws Exception { + try (final Jsonb jsonb = jsonb()) { + assertRead(jsonb); + assertWrite(jsonb); + assertRead(jsonb); + assertWrite(jsonb); + } + } + + /** + * Validate any caching done from a write operation + * does not alter the expected behavior of a read + * operation + */ + @Test + public void readAfterWrite() throws Exception { + try (final Jsonb jsonb = jsonb()) { + assertWrite(jsonb); + assertRead(jsonb); + assertWrite(jsonb); + assertRead(jsonb); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterOnClassDirectTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterOnClassDirectTest.java new file mode 100644 index 00000000..2249feba --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterOnClassDirectTest.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.array; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import org.junit.Ignore; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class ArrayAdapterOnClassDirectTest extends ArrayAdapterOnClassTest { + + public Jsonb jsonb() { + return JsonbBuilder.create(); + } + + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final String json = jsonb.toJson(email); + assertEquals("[\"test\",\"domain.com\",\"EmailClass.adaptToJson\"]", json); + assertEquals("EmailClass.adaptToJson", calls()); + } + + public void assertRead(final Jsonb jsonb) { + final String json = "[\"test\",\"domain.com\"]"; + final Email email = jsonb.fromJson(json, Email.class); + assertEquals("test@domain.com:EmailClass.adaptFromJson", email.toString()); + assertEquals("EmailClass.adaptFromJson", calls()); + } + + /** + * Fails as the adapter is not found + */ + @Test + @Ignore() + @Override + public void read() throws Exception { + super.read(); + } + + /** + * Fails as the adapter is not found + */ + @Test + @Ignore() + @Override + public void readAfterRead() throws Exception { + super.readAfterRead(); + } + + /** + * Fails as the adapter is not found on the first read + */ + @Test + @Ignore() + @Override + public void writeAfterRead() throws Exception { + super.writeAfterRead(); + } + + @Test + @Ignore() + @Override + public void readAfterWrite() throws Exception { + super.readAfterWrite(); + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterOnClassSimpleTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterOnClassSimpleTest.java new file mode 100644 index 00000000..f1d3913a --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterOnClassSimpleTest.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.array; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; + +import static org.junit.Assert.assertEquals; + +public class ArrayAdapterOnClassSimpleTest extends ArrayAdapterOnClassTest { + + public Jsonb jsonb() { + return JsonbBuilder.create(); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":[\"test\",\"domain.com\"]}"; + final ArrayAdapterPrecedenceConfigClassTest.Contact actual = jsonb.fromJson(json, ArrayAdapterPrecedenceConfigClassTest.Contact.class); + assertEquals("Contact{email=test@domain.com:EmailClass.adaptFromJson}", actual.toString()); + assertEquals("Contact.\n" + + "EmailClass.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final ArrayAdapterPrecedenceConfigClassTest.Contact contact = new ArrayAdapterPrecedenceConfigClassTest.Contact(); + contact.setEmail(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":[\"test\",\"domain.com\",\"EmailClass.adaptToJson\"]}", json); + assertEquals("Contact.getEmail\n" + + "EmailClass.adaptToJson", calls()); + } + + public static class Contact { + + private Email email; + + public Contact() { + CALLS.called(); + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } + +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterOnClassTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterOnClassTest.java new file mode 100644 index 00000000..dacc75e5 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterOnClassTest.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.array; + +import jakarta.json.bind.adapter.JsonbAdapter; +import jakarta.json.bind.annotation.JsonbTypeAdapter; +import org.apache.johnzon.jsonb.symmetry.Calls; +import org.apache.johnzon.jsonb.symmetry.SymmetryTest; +import org.junit.Before; + +public abstract class ArrayAdapterOnClassTest extends SymmetryTest { + + protected static final Calls CALLS = new Calls(); + + @Before + public void reset() { + CALLS.reset(); + } + + public static String calls() { + return CALLS.get(); + } + + @JsonbTypeAdapter(Adapter.EmailClass.class) + public static class Email { + final String user; + final String domain; + final String call; + + public Email(final String user, final String domain) { + this(user, domain, null); + } + + public Email(final String user, final String domain, final String call) { + this.user = user; + this.domain = domain; + this.call = call; + } + + @Override + public String toString() { + if (call == null) { + return user + "@" + domain; + } else { + return user + "@" + domain + ":" + call; + } + } + } + + public abstract static class Adapter implements JsonbAdapter { + + @Override + public String[] adaptToJson(final Email obj) { + return new String[]{obj.user, obj.domain, CALLS.called(this)}; + } + + @Override + public Email adaptFromJson(final String[] parts) { + return new Email(parts[0], parts[1], CALLS.called(this)); + } + + public static final class Getter extends Adapter { + } + + public static final class Setter extends Adapter { + } + + public static final class Field extends Adapter { + } + + public static final class Constructor extends Adapter { + } + + public static final class Config extends Adapter { + } + + public static final class EmailClass extends Adapter { + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassConstructorHasGetterFinalFieldTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassConstructorHasGetterFinalFieldTest.java new file mode 100644 index 00000000..ed4e5809 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassConstructorHasGetterFinalFieldTest.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.array; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Constructor + * - Config + * - Class + * + * Has + * - Getter + * - Final Field + * + * Outcome: + * - Constructor wins on read + * - EmailClass wins on write + * + * Question: + * - Should Config win on write? + * Adapters on the target type itself (Email) are effectively a hardcoded default adapter + * If a user wishes to alter this behavior for a specific operation via the config, why + * not let them? This would be the most (only?) convenient way to change behavior without + * sweeping code change. + */ +public class ArrayAdapterPrecedenceConfigClassConstructorHasGetterFinalFieldTest extends ArrayAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":[\"test\",\"domain.com\"]}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Constructor.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":[\"test\",\"domain.com\",\"EmailClass.adaptToJson\"]}", json); + assertEquals("Contact.getEmail\n" + + "EmailClass.adaptToJson", calls()); + } + + public static class Contact { + + private final Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassConstructorHasGetterSetterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassConstructorHasGetterSetterTest.java new file mode 100644 index 00000000..56fda0de --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassConstructorHasGetterSetterTest.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.array; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Constructor + * - Config + * - Class + * + * Has + * - Getter + * - Setter + * + * Outcome: + * - EmailClass wins on read + * - EmailClass wins on write + * - Constructor adapter is called, but overwritten + * + * Question: + * - Should Config win on write? + * Adapters on the target type itself (Email) are effectively a hardcoded default adapter + * If a user wishes to alter this behavior for a specific operation via the config, why + * not let them? This would be the most (only?) convenient way to change behavior without + * sweeping code change. + * + * - Shouldn't the constructor win on read? + * Seems like a clear bug + */ +public class ArrayAdapterPrecedenceConfigClassConstructorHasGetterSetterTest extends ArrayAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":[\"test\",\"domain.com\"]}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:EmailClass.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.\n" + + "EmailClass.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":[\"test\",\"domain.com\",\"EmailClass.adaptToJson\"]}", json); + assertEquals("Contact.getEmail\n" + + "EmailClass.adaptToJson", calls()); + } + + public static class Contact { + + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassDirectTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassDirectTest.java new file mode 100644 index 00000000..e3bfd572 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassDirectTest.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.array; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import org.junit.Ignore; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Config + * - Class + * + * + * Outcome: + * - Config wins on read + * - Config wins on write + */ +public class ArrayAdapterPrecedenceConfigClassDirectTest extends ArrayAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "[\"test\",\"domain.com\"]"; + final Email actual = jsonb.fromJson(json, Email.class); + assertEquals("test@domain.com:Config.adaptFromJson", actual.toString()); + assertEquals("Config.adaptFromJson", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + + final String json = jsonb.toJson(email); + assertEquals("[\"test\",\"domain.com\",\"Config.adaptToJson\"]", json); + assertEquals("Config.adaptToJson", calls()); + } + + @Test + @Ignore + @Override + public void read() throws Exception { + super.read(); + } + + @Test + @Ignore + @Override + public void readAfterRead() throws Exception { + super.readAfterRead(); + } + + @Test + @Ignore + @Override + public void readAfterWrite() throws Exception { + super.readAfterWrite(); + } + + @Test + @Ignore + @Override + public void writeAfterRead() throws Exception { + super.writeAfterRead(); + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassFieldConstructorHasGetterFinalFieldTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassFieldConstructorHasGetterFinalFieldTest.java new file mode 100644 index 00000000..a8fc42c2 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassFieldConstructorHasGetterFinalFieldTest.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.array; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Field + * - Constructor + * - Config + * - Class + * + * Has + * - Getter + * - Final Field + * + * Outcome: + * - Constructor wins on read + * - Field wins on write + */ +public class ArrayAdapterPrecedenceConfigClassFieldConstructorHasGetterFinalFieldTest extends ArrayAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":[\"test\",\"domain.com\"]}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Constructor.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":[\"test\",\"domain.com\",\"Field.adaptToJson\"]}", json); + assertEquals("Contact.getEmail\n" + + "Field.adaptToJson", calls()); + } + + public static class Contact { + + @JsonbTypeAdapter(Adapter.Field.class) + private final Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassFieldConstructorHasGetterSetterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassFieldConstructorHasGetterSetterTest.java new file mode 100644 index 00000000..9856bb57 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassFieldConstructorHasGetterSetterTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.array; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Field + * - Constructor + * - Config + * - Class + * + * Has + * - Getter + * - Setter + * + * Outcome: + * - Field wins on read + * - Field wins on write + * - Constructor adapter is called, but overwritten + */ +public class ArrayAdapterPrecedenceConfigClassFieldConstructorHasGetterSetterTest extends ArrayAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":[\"test\",\"domain.com\"]}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Field.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.\n" + + "Field.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":[\"test\",\"domain.com\",\"Field.adaptToJson\"]}", json); + assertEquals("Contact.getEmail\n" + + "Field.adaptToJson", calls()); + } + + public static class Contact { + + @JsonbTypeAdapter(Adapter.Field.class) + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassGetterFieldConstructorHasSetterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassGetterFieldConstructorHasSetterTest.java new file mode 100644 index 00000000..062b4151 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassGetterFieldConstructorHasSetterTest.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.array; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Config + * - Class + * - Constructor + * - Getter + * - Field + * + * Still has a setter + * + * Outcome + * + * - Field wins on read + * - Getter wins on write + * - Constructor adapter is called, but overwritten + */ +public class ArrayAdapterPrecedenceConfigClassGetterFieldConstructorHasSetterTest extends ArrayAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":[\"test\",\"domain.com\",\"Field.adaptToJson\"]}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Field.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.\n" + + "Field.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":[\"test\",\"domain.com\",\"Getter.adaptToJson\"]}", json); + assertEquals("Contact.getEmail\n" + + "Getter.adaptToJson", calls()); + } + + public static class Contact { + + @JsonbTypeAdapter(Adapter.Field.class) + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + @JsonbTypeAdapter(Adapter.Getter.class) + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassGetterSetterFieldConstructorTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassGetterSetterFieldConstructorTest.java new file mode 100644 index 00000000..ace2697c --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassGetterSetterFieldConstructorTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.array; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Field + * - Constructor + * - Getter + * - Setter + * - Config + * - Class + * + * Setter wins on read + * Getter wins on write + * + * Constructor adapter is called, but overwritten + */ +public class ArrayAdapterPrecedenceConfigClassGetterSetterFieldConstructorTest extends ArrayAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":[\"test\",\"domain.com\",\"Getter.adaptToJson\"]}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Setter.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.\n" + + "Setter.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":[\"test\",\"domain.com\",\"Getter.adaptToJson\"]}", json); + assertEquals("Contact.getEmail\n" + + "Getter.adaptToJson", calls()); + } + + public static class Contact { + + @JsonbTypeAdapter(Adapter.Field.class) + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + @JsonbTypeAdapter(Adapter.Getter.class) + public Email getEmail() { + CALLS.called(); + return email; + } + + @JsonbTypeAdapter(Adapter.Setter.class) + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassSetterFieldConstructorHasGetterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassSetterFieldConstructorHasGetterTest.java new file mode 100644 index 00000000..52eeae73 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassSetterFieldConstructorHasGetterTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.array; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Field + * - Constructor + * - Setter + * - Config + * - Class + * + * Still has a getter + * + * Outcome + * - Setter wins on read + * - Field wins on write + * - Constructor adapter is called, but overwritten + */ +public class ArrayAdapterPrecedenceConfigClassSetterFieldConstructorHasGetterTest extends ArrayAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":[\"test\",\"domain.com\",\"Field.adaptToJson\"]}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Setter.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.\n" + + "Setter.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":[\"test\",\"domain.com\",\"Field.adaptToJson\"]}", json); + assertEquals("Contact.getEmail\n" + + "Field.adaptToJson", calls()); + } + + public static class Contact { + + @JsonbTypeAdapter(Adapter.Field.class) + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + @JsonbTypeAdapter(Adapter.Setter.class) + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassTest.java new file mode 100644 index 00000000..f757f7a6 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/array/ArrayAdapterPrecedenceConfigClassTest.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.array; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Config + * - Class + * + * Has + * - Getter + * - Final Field + * + * Outcome: + * - EmailClass wins on read + * - EmailClass wins on write + * + * Question: + * - Should Config win on read and write? + * Adapters on the target type itself (Email) are effectively a hardcoded default adapter + * If a user wishes to alter this behavior for a specific operation via the config, why + * not let them? This would be the most (only?) convenient way to change behavior without + * sweeping code change. + */ +public class ArrayAdapterPrecedenceConfigClassTest extends ArrayAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":[\"test\",\"domain.com\"]}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:EmailClass.adaptFromJson}", actual.toString()); + assertEquals("Contact.\n" + + "EmailClass.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(); + contact.setEmail(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":[\"test\",\"domain.com\",\"EmailClass.adaptToJson\"]}", json); + assertEquals("Contact.getEmail\n" + + "EmailClass.adaptToJson", calls()); + } + + public static class Contact { + + private Email email; + + public Contact() { + CALLS.called(); + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterOnClassDirectTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterOnClassDirectTest.java new file mode 100644 index 00000000..a8905462 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterOnClassDirectTest.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.map; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import org.junit.Ignore; + +import static org.junit.Assert.assertEquals; + +@Ignore("java.lang.ClassCastException: Cannot cast sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl to java.lang.Class") +public class MapAdapterOnClassDirectTest extends MapAdapterOnClassTest { + + public Jsonb jsonb() { + return JsonbBuilder.create(); + } + + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final String json = jsonb.toJson(email); + assertEquals("{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"EmailClass.adaptToJson\"}", json); + assertEquals("EmailClass.adaptToJson", calls()); + } + + public void assertRead(final Jsonb jsonb) { + final String json = "{\"user\":\"test\",\"domain\":\"domain.com\"}"; + final Email email = jsonb.fromJson(json, Email.class); + assertEquals("test@domain.com:EmailClass.adaptFromJson", email.toString()); + assertEquals("EmailClass.adaptFromJson", calls()); + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterOnClassSimpleTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterOnClassSimpleTest.java new file mode 100644 index 00000000..68281d49 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterOnClassSimpleTest.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.map; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; + +import static org.junit.Assert.assertEquals; + +public class MapAdapterOnClassSimpleTest extends MapAdapterOnClassTest { + + public Jsonb jsonb() { + return JsonbBuilder.create(); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final MapAdapterPrecedenceConfigClassTest.Contact actual = jsonb.fromJson(json, MapAdapterPrecedenceConfigClassTest.Contact.class); + assertEquals("Contact{email=test@domain.com:EmailClass.adaptFromJson}", actual.toString()); + assertEquals("Contact.\n" + + "EmailClass.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final MapAdapterPrecedenceConfigClassTest.Contact contact = new MapAdapterPrecedenceConfigClassTest.Contact(); + contact.setEmail(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"EmailClass.adaptToJson\"}}", json); + assertEquals("Contact.getEmail\n" + + "EmailClass.adaptToJson", calls()); + } + + public static class Contact { + + private Email email; + + public Contact() { + CALLS.called(); + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } + +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterOnClassTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterOnClassTest.java new file mode 100644 index 00000000..396f35d7 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterOnClassTest.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.map; + +import jakarta.json.bind.adapter.JsonbAdapter; +import jakarta.json.bind.annotation.JsonbTypeAdapter; +import org.apache.johnzon.jsonb.symmetry.Calls; +import org.apache.johnzon.jsonb.symmetry.SymmetryTest; +import org.junit.Before; + +import java.util.LinkedHashMap; +import java.util.Map; + +public abstract class MapAdapterOnClassTest extends SymmetryTest { + + protected static final Calls CALLS = new Calls(); + + @Before + public void reset() { + CALLS.reset(); + } + + public static String calls() { + return CALLS.get(); + } + + @JsonbTypeAdapter(Adapter.EmailClass.class) + public static class Email { + final String user; + final String domain; + final String call; + + public Email(final String user, final String domain) { + this(user, domain, null); + } + + public Email(final String user, final String domain, final String call) { + this.user = user; + this.domain = domain; + this.call = call; + } + + @Override + public String toString() { + if (call == null) { + return user + "@" + domain; + } else { + return user + "@" + domain + ":" + call; + } + } + } + + public abstract static class Adapter implements JsonbAdapter> { + + @Override + public Map adaptToJson(final Email obj) { + final LinkedHashMap map = new LinkedHashMap<>(); + map.put("user", obj.user); + map.put("domain", obj.domain); + map.put("call", CALLS.called(this)); + return map; + } + + @Override + public Email adaptFromJson(final Map map) { + return new Email(map.get("user").toString(), + map.get("domain").toString(), + CALLS.called(this)); + } + + public static final class Getter extends Adapter { + } + + public static final class Setter extends Adapter { + } + + public static final class Field extends Adapter { + } + + public static final class Constructor extends Adapter { + } + + public static final class Config extends Adapter { + } + + public static final class EmailClass extends Adapter { + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassConstructorHasGetterFinalFieldTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassConstructorHasGetterFinalFieldTest.java new file mode 100644 index 00000000..66a53bf7 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassConstructorHasGetterFinalFieldTest.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.map; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Constructor + * - Config + * - Class + * + * Has + * - Getter + * - Final Field + * + * Outcome: + * - Constructor wins on read + * - EmailClass wins on write + * + * Question: + * - Should Config win on write? + * Adapters on the target type itself (Email) are effectively a hardcoded default adapter + * If a user wishes to alter this behavior for a specific operation via the config, why + * not let them? This would be the most (only?) convenient way to change behavior without + * sweeping code change. + */ +public class MapAdapterPrecedenceConfigClassConstructorHasGetterFinalFieldTest extends MapAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Constructor.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"EmailClass.adaptToJson\"}}", json); + assertEquals("Contact.getEmail\n" + + "EmailClass.adaptToJson", calls()); + } + + public static class Contact { + + private final Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassConstructorHasGetterSetterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassConstructorHasGetterSetterTest.java new file mode 100644 index 00000000..7eeb6d54 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassConstructorHasGetterSetterTest.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.map; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Constructor + * - Config + * - Class + * + * Has + * - Getter + * - Setter + * + * Outcome: + * - EmailClass wins on read + * - EmailClass wins on write + * - Constructor adapter is called, but overwritten + * + * Question: + * - Should Config win on write? + * Adapters on the target type itself (Email) are effectively a hardcoded default adapter + * If a user wishes to alter this behavior for a specific operation via the config, why + * not let them? This would be the most (only?) convenient way to change behavior without + * sweeping code change. + * + * - Shouldn't the constructor win on read? + * Seems like a clear bug + */ +public class MapAdapterPrecedenceConfigClassConstructorHasGetterSetterTest extends MapAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:EmailClass.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.\n" + + "EmailClass.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"EmailClass.adaptToJson\"}}", json); + assertEquals("Contact.getEmail\n" + + "EmailClass.adaptToJson", calls()); + } + + public static class Contact { + + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassDirectTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassDirectTest.java new file mode 100644 index 00000000..3d59920d --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassDirectTest.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.map; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import org.junit.Ignore; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Config + * - Class + * + * + * Outcome: + * - Reads fail + * - Config wins on write + */ +public class MapAdapterPrecedenceConfigClassDirectTest extends MapAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"user\":\"test\",\"domain\":\"domain.com\"}"; + final Email actual = jsonb.fromJson(json, Email.class); + assertEquals("test@domain.com:Config.adaptFromJson", actual.toString()); + assertEquals("Config.adaptFromJson", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + + final String json = jsonb.toJson(email); + assertEquals("{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"Config.adaptToJson\"}", json); + assertEquals("Config.adaptToJson", calls()); + } + + /** + * java.lang.ClassCastException: Cannot cast sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl to java.lang.Class + * + * at java.base/java.lang.Class.cast(Class.java:3889) + * at org.apache.johnzon.jsonb.JsonbAccessMode.isReversedAdapter(JsonbAccessMode.java:875) + */ + @Test + @Ignore + @Override + public void read() throws Exception { + super.read(); + } + + /** + * java.lang.ClassCastException: Cannot cast sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl to java.lang.Class + * + * at java.base/java.lang.Class.cast(Class.java:3889) + * at org.apache.johnzon.jsonb.JsonbAccessMode.isReversedAdapter(JsonbAccessMode.java:875) + */ + @Test + @Ignore + @Override + public void readAfterRead() throws Exception { + super.readAfterRead(); + } + + /** + * java.lang.ClassCastException: Cannot cast sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl to java.lang.Class + * + * at java.base/java.lang.Class.cast(Class.java:3889) + * at org.apache.johnzon.jsonb.JsonbAccessMode.isReversedAdapter(JsonbAccessMode.java:875) + */ + @Test + @Ignore + @Override + public void readAfterWrite() throws Exception { + super.readAfterWrite(); + } + + /** + * java.lang.ClassCastException: Cannot cast sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl to java.lang.Class + * + * at java.base/java.lang.Class.cast(Class.java:3889) + * at org.apache.johnzon.jsonb.JsonbAccessMode.isReversedAdapter(JsonbAccessMode.java:875) + */ + @Test + @Ignore + @Override + public void writeAfterRead() throws Exception { + super.writeAfterRead(); + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassFieldConstructorHasGetterFinalFieldTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassFieldConstructorHasGetterFinalFieldTest.java new file mode 100644 index 00000000..581ffa4a --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassFieldConstructorHasGetterFinalFieldTest.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.map; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Field + * - Constructor + * - Config + * - Class + * + * Has + * - Getter + * - Final Field + * + * Outcome: + * - Constructor wins on read + * - Field wins on write + */ +public class MapAdapterPrecedenceConfigClassFieldConstructorHasGetterFinalFieldTest extends MapAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Constructor.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"Field.adaptToJson\"}}", json); + assertEquals("Contact.getEmail\n" + + "Field.adaptToJson", calls()); + } + + public static class Contact { + + @JsonbTypeAdapter(Adapter.Field.class) + private final Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassFieldConstructorHasGetterSetterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassFieldConstructorHasGetterSetterTest.java new file mode 100644 index 00000000..67e4cab4 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassFieldConstructorHasGetterSetterTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.map; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Field + * - Constructor + * - Config + * - Class + * + * Has + * - Getter + * - Setter + * + * Outcome: + * - Field wins on read + * - Field wins on write + * - Constructor adapter is called, but overwritten + */ +public class MapAdapterPrecedenceConfigClassFieldConstructorHasGetterSetterTest extends MapAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Field.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.\n" + + "Field.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"Field.adaptToJson\"}}", json); + assertEquals("Contact.getEmail\n" + + "Field.adaptToJson", calls()); + } + + public static class Contact { + + @JsonbTypeAdapter(Adapter.Field.class) + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassGetterFieldConstructorHasSetterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassGetterFieldConstructorHasSetterTest.java new file mode 100644 index 00000000..6ba33d89 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassGetterFieldConstructorHasSetterTest.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.map; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Config + * - Class + * - Constructor + * - Getter + * - Field + * + * Still has a setter + * + * Outcome + * + * - Field wins on read + * - Getter wins on write + * - Constructor adapter is called, but overwritten + */ +public class MapAdapterPrecedenceConfigClassGetterFieldConstructorHasSetterTest extends MapAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Field.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.\n" + + "Field.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"Getter.adaptToJson\"}}", json); + assertEquals("Contact.getEmail\n" + + "Getter.adaptToJson", calls()); + } + + public static class Contact { + + @JsonbTypeAdapter(Adapter.Field.class) + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + @JsonbTypeAdapter(Adapter.Getter.class) + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassGetterSetterFieldConstructorTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassGetterSetterFieldConstructorTest.java new file mode 100644 index 00000000..ece34685 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassGetterSetterFieldConstructorTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.map; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Field + * - Constructor + * - Getter + * - Setter + * - Config + * - Class + * + * Setter wins on read + * Getter wins on write + * + * Constructor adapter is called, but overwritten + */ +public class MapAdapterPrecedenceConfigClassGetterSetterFieldConstructorTest extends MapAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Setter.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.\n" + + "Setter.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"Getter.adaptToJson\"}}", json); + assertEquals("Contact.getEmail\n" + + "Getter.adaptToJson", calls()); + } + + public static class Contact { + + @JsonbTypeAdapter(Adapter.Field.class) + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + @JsonbTypeAdapter(Adapter.Getter.class) + public Email getEmail() { + CALLS.called(); + return email; + } + + @JsonbTypeAdapter(Adapter.Setter.class) + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassSetterFieldConstructorHasGetterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassSetterFieldConstructorHasGetterTest.java new file mode 100644 index 00000000..e02f03cd --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassSetterFieldConstructorHasGetterTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.map; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Field + * - Constructor + * - Setter + * - Config + * - Class + * + * Still has a getter + * + * Outcome + * - Setter wins on read + * - Field wins on write + * - Constructor adapter is called, but overwritten + */ +public class MapAdapterPrecedenceConfigClassSetterFieldConstructorHasGetterTest extends MapAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Setter.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.\n" + + "Setter.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"Field.adaptToJson\"}}", json); + assertEquals("Contact.getEmail\n" + + "Field.adaptToJson", calls()); + } + + public static class Contact { + + @JsonbTypeAdapter(Adapter.Field.class) + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + @JsonbTypeAdapter(Adapter.Setter.class) + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassTest.java new file mode 100644 index 00000000..b4e5ee13 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/map/MapAdapterPrecedenceConfigClassTest.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.map; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Config + * - Class + * + * Has + * - Getter + * - Final Field + * + * Outcome: + * - EmailClass wins on read + * - EmailClass wins on write + * + * Question: + * - Should Config win on read and write? + * Adapters on the target type itself (Email) are effectively a hardcoded default adapter + * If a user wishes to alter this behavior for a specific operation via the config, why + * not let them? This would be the most (only?) convenient way to change behavior without + * sweeping code change. + */ +public class MapAdapterPrecedenceConfigClassTest extends MapAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:EmailClass.adaptFromJson}", actual.toString()); + assertEquals("Contact.\n" + + "EmailClass.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(); + contact.setEmail(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"EmailClass.adaptToJson\"}}", json); + assertEquals("Contact.getEmail\n" + + "EmailClass.adaptToJson", calls()); + } + + public static class Contact { + + private Email email; + + public Contact() { + CALLS.called(); + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterOnClassDirectTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterOnClassDirectTest.java new file mode 100644 index 00000000..7266a76e --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterOnClassDirectTest.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.string; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import org.junit.Ignore; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class StringAdapterOnClassDirectTest extends StringAdapterOnClassTest { + + public Jsonb jsonb() { + return JsonbBuilder.create(); + } + + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final String json = jsonb.toJson(email); + assertEquals("\"test@domain.com:EmailClass.adaptToJson\"", json); + assertEquals("EmailClass.adaptToJson", calls()); + } + + public void assertRead(final Jsonb jsonb) { + final String json = "\"test@domain.com\""; + final Email email = jsonb.fromJson(json, Email.class); + assertEquals("test@domain.com:EmailClass.adaptFromJson", email.toString()); + assertEquals("EmailClass.adaptFromJson", calls()); + } + + /** + * Fails as the adapter is not found + */ + @Test + @Ignore() + @Override + public void read() throws Exception { + super.read(); + } + + /** + * Fails as the adapter is not found + */ + @Test + @Ignore() + @Override + public void readAfterRead() throws Exception { + super.readAfterRead(); + } + + /** + * Fails as the adapter is not found on the first read + */ + @Test + @Ignore() + @Override + public void writeAfterRead() throws Exception { + super.writeAfterRead(); + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterOnClassSimpleTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterOnClassSimpleTest.java new file mode 100644 index 00000000..011cab19 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterOnClassSimpleTest.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.string; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; + +import static org.junit.Assert.assertEquals; + +public class StringAdapterOnClassSimpleTest extends StringAdapterOnClassTest { + + public Jsonb jsonb() { + return JsonbBuilder.create(); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":\"test@domain.com\"}"; + final StringAdapterPrecedenceConfigClassTest.Contact actual = jsonb.fromJson(json, StringAdapterPrecedenceConfigClassTest.Contact.class); + assertEquals("Contact{email=test@domain.com:EmailClass.adaptFromJson}", actual.toString()); + assertEquals("Contact.\n" + + "EmailClass.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final StringAdapterPrecedenceConfigClassTest.Contact contact = new StringAdapterPrecedenceConfigClassTest.Contact(); + contact.setEmail(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":\"test@domain.com:EmailClass.adaptToJson\"}", json); + assertEquals("Contact.getEmail\n" + + "EmailClass.adaptToJson", calls()); + } + + public static class Contact { + + private Email email; + + public Contact() { + CALLS.called(); + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } + +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterOnClassTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterOnClassTest.java new file mode 100644 index 00000000..3f3ba0e7 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterOnClassTest.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.string; + +import jakarta.json.bind.adapter.JsonbAdapter; +import jakarta.json.bind.annotation.JsonbTypeAdapter; +import org.apache.johnzon.jsonb.symmetry.Calls; +import org.apache.johnzon.jsonb.symmetry.SymmetryTest; +import org.junit.Before; + +public abstract class StringAdapterOnClassTest extends SymmetryTest { + + protected static final Calls CALLS = new Calls(); + + @Before + public void reset() { + CALLS.reset(); + } + + public static String calls() { + return CALLS.get(); + } + + @JsonbTypeAdapter(Adapter.EmailClass.class) + public static class Email { + final String user; + final String domain; + final String call; + + public Email(final String user, final String domain) { + this(user, domain, null); + } + + public Email(final String user, final String domain, final String call) { + this.user = user; + this.domain = domain; + this.call = call; + } + + @Override + public String toString() { + if (call == null) { + return user + "@" + domain; + } else { + return user + "@" + domain + ":" + call; + } + } + } + + public abstract static class Adapter implements JsonbAdapter { + + @Override + public String adaptToJson(final Email obj) { + return obj.user + "@" + obj.domain + ":" + CALLS.called(this); + } + + @Override + public Email adaptFromJson(final String obj) { + final String[] parts = obj.split("[@:]"); + return new Email(parts[0], parts[1], CALLS.called(this)); + } + + public static final class Getter extends Adapter { + } + + public static final class Setter extends Adapter { + } + + public static final class Field extends Adapter { + } + + public static final class Constructor extends Adapter { + } + + public static final class Config extends Adapter { + } + + public static final class EmailClass extends Adapter { + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassConstructorHasGetterFinalFieldTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassConstructorHasGetterFinalFieldTest.java new file mode 100644 index 00000000..0880bef1 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassConstructorHasGetterFinalFieldTest.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.string; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Constructor + * - Config + * - Class + * + * Has + * - Getter + * - Final Field + * + * Outcome: + * - Constructor wins on read + * - EmailClass wins on write + * + * Question: + * - Should Config win on write? + * Adapters on the target type itself (Email) are effectively a hardcoded default adapter + * If a user wishes to alter this behavior for a specific operation via the config, why + * not let them? This would be the most (only?) convenient way to change behavior without + * sweeping code change. + */ +public class StringAdapterPrecedenceConfigClassConstructorHasGetterFinalFieldTest extends StringAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":\"test@domain.com\"}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Constructor.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":\"test@domain.com:EmailClass.adaptToJson\"}", json); + assertEquals("Contact.getEmail\n" + + "EmailClass.adaptToJson", calls()); + } + + public static class Contact { + + private final Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassConstructorHasGetterSetterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassConstructorHasGetterSetterTest.java new file mode 100644 index 00000000..4321370d --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassConstructorHasGetterSetterTest.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.string; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Constructor + * - Config + * - Class + * + * Has + * - Getter + * - Setter + * + * Outcome: + * - EmailClass wins on read + * - EmailClass wins on write + * - Constructor adapter is called, but overwritten + * + * Question: + * - Should Config win on write? + * Adapters on the target type itself (Email) are effectively a hardcoded default adapter + * If a user wishes to alter this behavior for a specific operation via the config, why + * not let them? This would be the most (only?) convenient way to change behavior without + * sweeping code change. + * + * - Shouldn't the constructor win on read? + * Seems like a clear bug + */ +public class StringAdapterPrecedenceConfigClassConstructorHasGetterSetterTest extends StringAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":\"test@domain.com\"}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:EmailClass.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.\n" + + "EmailClass.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":\"test@domain.com:EmailClass.adaptToJson\"}", json); + assertEquals("Contact.getEmail\n" + + "EmailClass.adaptToJson", calls()); + } + + public static class Contact { + + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassDirectTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassDirectTest.java new file mode 100644 index 00000000..40970876 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassDirectTest.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.string; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Config + * - Class + * + * + * Outcome: + * - Config wins on read + * - Config wins on write + */ +public class StringAdapterPrecedenceConfigClassDirectTest extends StringAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "\"test@domain.com\""; + final Email actual = jsonb.fromJson(json, Email.class); + assertEquals("test@domain.com:Config.adaptFromJson", actual.toString()); + assertEquals("Config.adaptFromJson", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + + final String json = jsonb.toJson(email); + assertEquals("\"test@domain.com:Config.adaptToJson\"", json); + assertEquals("Config.adaptToJson", calls()); + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassFieldConstructorHasGetterFinalFieldTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassFieldConstructorHasGetterFinalFieldTest.java new file mode 100644 index 00000000..38e03141 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassFieldConstructorHasGetterFinalFieldTest.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.string; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Field + * - Constructor + * - Config + * - Class + * + * Has + * - Getter + * - Final Field + * + * Outcome: + * - Constructor wins on read + * - Field wins on write + */ +public class StringAdapterPrecedenceConfigClassFieldConstructorHasGetterFinalFieldTest extends StringAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":\"test@domain.com\"}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Constructor.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":\"test@domain.com:Field.adaptToJson\"}", json); + assertEquals("Contact.getEmail\n" + + "Field.adaptToJson", calls()); + } + + public static class Contact { + + @JsonbTypeAdapter(Adapter.Field.class) + private final Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassFieldConstructorHasGetterSetterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassFieldConstructorHasGetterSetterTest.java new file mode 100644 index 00000000..cf91f68c --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassFieldConstructorHasGetterSetterTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.string; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Field + * - Constructor + * - Config + * - Class + * + * Has + * - Getter + * - Setter + * + * Outcome: + * - Field wins on read + * - Field wins on write + * - Constructor adapter is called, but overwritten + */ +public class StringAdapterPrecedenceConfigClassFieldConstructorHasGetterSetterTest extends StringAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":\"test@domain.com\"}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Field.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.\n" + + "Field.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":\"test@domain.com:Field.adaptToJson\"}", json); + assertEquals("Contact.getEmail\n" + + "Field.adaptToJson", calls()); + } + + public static class Contact { + + @JsonbTypeAdapter(Adapter.Field.class) + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassGetterFieldConstructorHasSetterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassGetterFieldConstructorHasSetterTest.java new file mode 100644 index 00000000..ac50bf12 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassGetterFieldConstructorHasSetterTest.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.string; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Config + * - Class + * - Constructor + * - Getter + * - Field + * + * Still has a setter + * + * Outcome + * + * - Field wins on read + * - Getter wins on write + * - Constructor adapter is called, but overwritten + */ +public class StringAdapterPrecedenceConfigClassGetterFieldConstructorHasSetterTest extends StringAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":\"test@domain.com\"}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Field.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.\n" + + "Field.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":\"test@domain.com:Getter.adaptToJson\"}", json); + assertEquals("Contact.getEmail\n" + + "Getter.adaptToJson", calls()); + } + + public static class Contact { + + @JsonbTypeAdapter(Adapter.Field.class) + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + @JsonbTypeAdapter(Adapter.Getter.class) + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassGetterSetterFieldConstructorTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassGetterSetterFieldConstructorTest.java new file mode 100644 index 00000000..c0e34f6b --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassGetterSetterFieldConstructorTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.string; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Field + * - Constructor + * - Getter + * - Setter + * - Config + * - Class + * + * Setter wins on read + * Getter wins on write + * + * Constructor adapter is called, but overwritten + */ +public class StringAdapterPrecedenceConfigClassGetterSetterFieldConstructorTest extends StringAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":\"test@domain.com\"}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Setter.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.\n" + + "Setter.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":\"test@domain.com:Getter.adaptToJson\"}", json); + assertEquals("Contact.getEmail\n" + + "Getter.adaptToJson", calls()); + } + + public static class Contact { + + @JsonbTypeAdapter(Adapter.Field.class) + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + @JsonbTypeAdapter(Adapter.Getter.class) + public Email getEmail() { + CALLS.called(); + return email; + } + + @JsonbTypeAdapter(Adapter.Setter.class) + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassSetterFieldConstructorHasGetterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassSetterFieldConstructorHasGetterTest.java new file mode 100644 index 00000000..6182694a --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassSetterFieldConstructorHasGetterTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.string; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Field + * - Constructor + * - Setter + * - Config + * - Class + * + * Still has a getter + * + * Outcome + * - Setter wins on read + * - Field wins on write + * - Constructor adapter is called, but overwritten + */ +public class StringAdapterPrecedenceConfigClassSetterFieldConstructorHasGetterTest extends StringAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":\"test@domain.com\"}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Setter.adaptFromJson}", actual.toString()); + assertEquals("Constructor.adaptFromJson\n" + + "Contact.\n" + + "Setter.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":\"test@domain.com:Field.adaptToJson\"}", json); + assertEquals("Contact.getEmail\n" + + "Field.adaptToJson", calls()); + } + + public static class Contact { + + @JsonbTypeAdapter(Adapter.Field.class) + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeAdapter(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + @JsonbTypeAdapter(Adapter.Setter.class) + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassTest.java new file mode 100644 index 00000000..2bcea10d --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/adapter/string/StringAdapterPrecedenceConfigClassTest.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.adapter.string; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Config + * - Class + * + * Has + * - Getter + * - Final Field + * + * Outcome: + * - EmailClass wins on read + * - EmailClass wins on write + * + * Question: + * - Should Config win on read and write? + * Adapters on the target type itself (Email) are effectively a hardcoded default adapter + * If a user wishes to alter this behavior for a specific operation via the config, why + * not let them? This would be the most (only?) convenient way to change behavior without + * sweeping code change. + */ +public class StringAdapterPrecedenceConfigClassTest extends StringAdapterOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig().withAdapters(new Adapter.Config())); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":\"test@domain.com\"}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:EmailClass.adaptFromJson}", actual.toString()); + assertEquals("Contact.\n" + + "EmailClass.adaptFromJson\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(); + contact.setEmail(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":\"test@domain.com:EmailClass.adaptToJson\"}", json); + assertEquals("Contact.getEmail\n" + + "EmailClass.adaptToJson", calls()); + } + + public static class Contact { + + private Email email; + + public Contact() { + CALLS.called(); + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerOnClassDirectTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerOnClassDirectTest.java new file mode 100644 index 00000000..b5138fa2 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerOnClassDirectTest.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.serializer; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; + +import static org.junit.Assert.assertEquals; + +public class SerializerOnClassDirectTest extends SerializerOnClassTest { + + public Jsonb jsonb() { + return JsonbBuilder.create(); + } + + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final String json = jsonb.toJson(email); + assertEquals("{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"EmailClass.serialize\"}", json); + assertEquals("EmailClass.serialize", calls()); + } + + public void assertRead(final Jsonb jsonb) { + final String json = "{\"user\":\"test\",\"domain\":\"domain.com\"}"; + final Email email = jsonb.fromJson(json, Email.class); + assertEquals("test@domain.com:EmailClass.deserialize", email.toString()); + assertEquals("EmailClass.deserialize", calls()); + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerOnClassSimpleTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerOnClassSimpleTest.java new file mode 100644 index 00000000..ae62f0ad --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerOnClassSimpleTest.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.serializer; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; + +import static org.junit.Assert.assertEquals; + +public class SerializerOnClassSimpleTest extends SerializerOnClassTest { + + public Jsonb jsonb() { + return JsonbBuilder.create(); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final SerializerPrecedenceConfigClassTest.Contact actual = jsonb.fromJson(json, SerializerPrecedenceConfigClassTest.Contact.class); + assertEquals("Contact{email=test@domain.com:EmailClass.deserialize}", actual.toString()); + assertEquals("Contact.\n" + + "EmailClass.deserialize\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final SerializerPrecedenceConfigClassTest.Contact contact = new SerializerPrecedenceConfigClassTest.Contact(); + contact.setEmail(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"EmailClass.serialize\"}}", json); + assertEquals("Contact.getEmail\n" + + "EmailClass.serialize", calls()); + } + + public static class Contact { + + private Email email; + + public Contact() { + CALLS.called(); + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } + +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerOnClassTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerOnClassTest.java new file mode 100644 index 00000000..a008e8a6 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerOnClassTest.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.serializer; + +import jakarta.json.bind.annotation.JsonbTypeDeserializer; +import jakarta.json.bind.annotation.JsonbTypeSerializer; +import jakarta.json.bind.serializer.DeserializationContext; +import jakarta.json.bind.serializer.JsonbDeserializer; +import jakarta.json.bind.serializer.JsonbSerializer; +import jakarta.json.bind.serializer.SerializationContext; +import jakarta.json.stream.JsonGenerator; +import jakarta.json.stream.JsonParser; +import org.apache.johnzon.jsonb.symmetry.Calls; +import org.apache.johnzon.jsonb.symmetry.SymmetryTest; +import org.junit.Before; + +import java.lang.reflect.Type; + +public abstract class SerializerOnClassTest extends SymmetryTest { + + protected static final Calls CALLS = new Calls(); + + @Before + public void reset() { + CALLS.reset(); + } + + public static String calls() { + return CALLS.get(); + } + + @JsonbTypeDeserializer(Adapter.EmailClass.class) + @JsonbTypeSerializer(Adapter.EmailClass.class) + public static class Email { + final String user; + final String domain; + final String call; + + public Email(final String user, final String domain) { + this(user, domain, null); + } + + public Email(final String user, final String domain, final String call) { + this.user = user; + this.domain = domain; + this.call = call; + } + + @Override + public String toString() { + if (call == null) { + return user + "@" + domain; + } else { + return user + "@" + domain + ":" + call; + } + } + } + + public abstract static class Adapter implements JsonbSerializer, JsonbDeserializer { + + @Override + public void serialize(final Email obj, final JsonGenerator generator, final SerializationContext ctx) { + final String call = CALLS.called(this); + generator.writeStartObject(); + generator.write("user", obj.user); + generator.write("domain", obj.domain); + generator.write("call", call); + generator.writeEnd(); + } + + @Override + public Email deserialize(final JsonParser parser, final DeserializationContext ctx, final Type type) { + String user = null; + String domain = null; + + while (parser.hasNext()) { + final JsonParser.Event event = parser.next(); + if (event == JsonParser.Event.KEY_NAME) { + final String key = parser.getString(); + parser.next(); + switch (key) { + case "user": + user = parser.getString(); + break; + case "domain": + domain = parser.getString(); + break; + // skip "call" + default: //ignore + } + } else if (event == JsonParser.Event.END_OBJECT) { + break; + } + } + + final String call = CALLS.called(this); + return new Email(user, domain, call); + } + + public static final class Getter extends Adapter { + } + + public static final class Setter extends Adapter { + } + + public static final class Field extends Adapter { + } + + public static final class Constructor extends Adapter { + } + + public static final class Config extends Adapter { + } + + public static final class EmailClass extends Adapter { + } + } +} \ No newline at end of file diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceClassConstructorHasGetterFinalFieldTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceClassConstructorHasGetterFinalFieldTest.java new file mode 100644 index 00000000..1c635c62 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceClassConstructorHasGetterFinalFieldTest.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.serializer; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeDeserializer; + +import static org.junit.Assert.assertEquals; + +/** + * Adapter on + * - Constructor + * - Class + * + * Has + * - Getter + * - Final Field + * + * Outcome: + * - Constructor wins on read + * - Email wins on write + */ +public class SerializerPrecedenceClassConstructorHasGetterFinalFieldTest extends SerializerOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Constructor.deserialize}", actual.toString()); + assertEquals("Constructor.deserialize\n" + + "Contact.", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"EmailClass.serialize\"}}", json); + assertEquals("Contact.getEmail\n" + + "EmailClass.serialize", calls()); + } + + public static class Contact { + + private final Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeDeserializer(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceClassConstructorHasGetterSetterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceClassConstructorHasGetterSetterTest.java new file mode 100644 index 00000000..ae2c5193 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceClassConstructorHasGetterSetterTest.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.serializer; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeDeserializer; + +import static org.junit.Assert.assertEquals; + +/** + * Adapters on + * - Constructor + * - Class + * + * Has + * - Getter + * - Setter + * + * Outcome: + * - EmailClass wins on read + * - EmailClass wins on write + * - Constructor adapter is called, but overwritten + * + * Possible bug: + * - Shouldn't the constructor win on read? + */ +public class SerializerPrecedenceClassConstructorHasGetterSetterTest extends SerializerOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:EmailClass.deserialize}", actual.toString()); + assertEquals("Constructor.deserialize\n" + + "Contact.\n" + + "EmailClass.deserialize\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"EmailClass.serialize\"}}", json); + assertEquals("Contact.getEmail\n" + + "EmailClass.serialize", calls()); + } + + public static class Contact { + + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeDeserializer(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassConstructorHasGetterFinalFieldTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassConstructorHasGetterFinalFieldTest.java new file mode 100644 index 00000000..234b0376 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassConstructorHasGetterFinalFieldTest.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.serializer; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeDeserializer; + +import static org.junit.Assert.assertEquals; + +/** + * Adapter on + * - Constructor + * - Config + * - Class + * + * Has + * - Getter + * - Final Field + * + * Outcome: + * - Constructor wins on read + * - Config wins on write + * + * Inconsistency: + * - Equivalent test for JsonbTypeAdapter the EmailClass adapter wins on write (likely bug in JsonbTypeAdapter code) + */ +public class SerializerPrecedenceConfigClassConstructorHasGetterFinalFieldTest extends SerializerOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig() + .withSerializers(new Adapter.Config()) + .withDeserializers(new Adapter.Config()) + ); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Constructor.deserialize}", actual.toString()); + assertEquals("Constructor.deserialize\n" + + "Contact.", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"Config.serialize\"}}", json); + assertEquals("Contact.getEmail\n" + + "Config.serialize", calls()); + } + + public static class Contact { + + private final Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeDeserializer(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassConstructorHasGetterSetterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassConstructorHasGetterSetterTest.java new file mode 100644 index 00000000..17837786 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassConstructorHasGetterSetterTest.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.serializer; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeDeserializer; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Constructor + * - Config + * - Class + * + * Has + * - Getter + * - Setter + * + * Outcome: + * - Config wins on read + * - Config wins on write + * - Constructor adapter is called, but overwritten + * + * Inconsistency: + * - Equivalent test for JsonbTypeAdapter the EmailClass adapter wins (likely bug in JsonbTypeAdapter code) + * + * Possible bug: + * - Shouldn't the constructor win on read? + */ +public class SerializerPrecedenceConfigClassConstructorHasGetterSetterTest extends SerializerOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig() + .withSerializers(new Adapter.Config()) + .withDeserializers(new Adapter.Config()) + ); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Config.deserialize}", actual.toString()); + assertEquals("Constructor.deserialize\n" + + "Contact.\n" + + "Config.deserialize\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"Config.serialize\"}}", json); + assertEquals("Contact.getEmail\n" + + "Config.serialize", calls()); + } + + public static class Contact { + + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeDeserializer(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassDirectTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassDirectTest.java new file mode 100644 index 00000000..2f20e7c6 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassDirectTest.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.serializer; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; + +import static org.junit.Assert.assertEquals; + +/** + * JsonbTypeAdapter on + * - Config + * - Class + * + * + * Outcome: + * - Config wins on read + * - Config wins on write + */ +public class SerializerPrecedenceConfigClassDirectTest extends SerializerOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig() + .withSerializers(new Adapter.Config()) + .withDeserializers(new Adapter.Config()) + ); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"user\":\"test\",\"domain\":\"domain.com\"}"; + final Email actual = jsonb.fromJson(json, Email.class); + assertEquals("test@domain.com:Config.deserialize", actual.toString()); + assertEquals("Config.deserialize", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + + final String json = jsonb.toJson(email); + assertEquals("{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"Config.serialize\"}", json); + assertEquals("Config.serialize", calls()); + } + +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassFieldConstructorHasGetterFinalFieldTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassFieldConstructorHasGetterFinalFieldTest.java new file mode 100644 index 00000000..7df7b86a --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassFieldConstructorHasGetterFinalFieldTest.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.serializer; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeDeserializer; +import jakarta.json.bind.annotation.JsonbTypeSerializer; + +import static org.junit.Assert.assertEquals; + +/** + * Adapter on + * - Field + * - Constructor + * - Config + * - Class + * + * Has + * - Getter + * - Final Field + * + * Outcome: + * - Constructor wins on read + * - Field wins on write + */ +public class SerializerPrecedenceConfigClassFieldConstructorHasGetterFinalFieldTest extends SerializerOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig() + .withSerializers(new Adapter.Config()) + .withDeserializers(new Adapter.Config()) + ); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Constructor.deserialize}", actual.toString()); + assertEquals("Constructor.deserialize\n" + + "Contact.", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"Field.serialize\"}}", json); + assertEquals("Contact.getEmail\n" + + "Field.serialize", calls()); + } + + public static class Contact { + + @JsonbTypeDeserializer(Adapter.Field.class) + @JsonbTypeSerializer(Adapter.Field.class) + private final Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeDeserializer(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassFieldConstructorHasGetterSetterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassFieldConstructorHasGetterSetterTest.java new file mode 100644 index 00000000..d48e6d73 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassFieldConstructorHasGetterSetterTest.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.serializer; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeDeserializer; +import jakarta.json.bind.annotation.JsonbTypeSerializer; + +import static org.junit.Assert.assertEquals; + +/** + * Adapter on + * - Field + * - Constructor + * - Config + * - Class + * + * Has + * - Getter + * - Setter + * + * Outcome: + * - Field wins on read + * - Field wins on write + * - Constructor adapter is called, but overwritten + */ +public class SerializerPrecedenceConfigClassFieldConstructorHasGetterSetterTest extends SerializerOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig() + .withSerializers(new Adapter.Config()) + .withDeserializers(new Adapter.Config()) + ); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Field.deserialize}", actual.toString()); + assertEquals("Constructor.deserialize\n" + + "Contact.\n" + + "Field.deserialize\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"Field.serialize\"}}", json); + assertEquals("Contact.getEmail\n" + + "Field.serialize", calls()); + } + + public static class Contact { + + @JsonbTypeDeserializer(Adapter.Field.class) + @JsonbTypeSerializer(Adapter.Field.class) + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeDeserializer(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassGetterFieldConstructorHasSetterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassGetterFieldConstructorHasSetterTest.java new file mode 100644 index 00000000..a309d339 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassGetterFieldConstructorHasSetterTest.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.serializer; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeDeserializer; +import jakarta.json.bind.annotation.JsonbTypeSerializer; + +import static org.junit.Assert.assertEquals; + +/** + * Adapter on + * - Config + * - Class + * - Constructor + * - Getter + * - Field + * + * Still has a setter + * + * Outcome + * + * - Field wins on read + * - Getter wins on write + * - Constructor adapter is called, but overwritten + */ +public class SerializerPrecedenceConfigClassGetterFieldConstructorHasSetterTest extends SerializerOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig() + .withSerializers(new Adapter.Config()) + .withDeserializers(new Adapter.Config()) + ); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Field.deserialize}", actual.toString()); + assertEquals("Constructor.deserialize\n" + + "Contact.\n" + + "Field.deserialize\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"Getter.serialize\"}}", json); + assertEquals("Contact.getEmail\n" + + "Getter.serialize", calls()); + } + + public static class Contact { + + @JsonbTypeDeserializer(Adapter.Field.class) + @JsonbTypeSerializer(Adapter.Field.class) + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeDeserializer(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + @JsonbTypeSerializer(Adapter.Getter.class) + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassGetterSetterFieldConstructorTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassGetterSetterFieldConstructorTest.java new file mode 100644 index 00000000..d906f214 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassGetterSetterFieldConstructorTest.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.serializer; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeDeserializer; +import jakarta.json.bind.annotation.JsonbTypeSerializer; + +import static org.junit.Assert.assertEquals; + +/** + * Adapter on + * - Field + * - Constructor + * - Getter + * - Setter + * - Config + * - Class + * + * Setter wins on read + * Getter wins on write + * + * Constructor adapter is called, but overwritten + */ +public class SerializerPrecedenceConfigClassGetterSetterFieldConstructorTest extends SerializerOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig() + .withSerializers(new Adapter.Config()) + .withDeserializers(new Adapter.Config()) + ); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Setter.deserialize}", actual.toString()); + assertEquals("Constructor.deserialize\n" + + "Contact.\n" + + "Setter.deserialize\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"Getter.serialize\"}}", json); + assertEquals("Contact.getEmail\n" + + "Getter.serialize", calls()); + } + + public static class Contact { + + @JsonbTypeDeserializer(Adapter.Field.class) + @JsonbTypeSerializer(Adapter.Field.class) + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeDeserializer(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + @JsonbTypeSerializer(Adapter.Getter.class) + public Email getEmail() { + CALLS.called(); + return email; + } + + @JsonbTypeDeserializer(Adapter.Setter.class) + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassSetterFieldConstructorHasGetterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassSetterFieldConstructorHasGetterTest.java new file mode 100644 index 00000000..3ded7faf --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassSetterFieldConstructorHasGetterTest.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.serializer; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeDeserializer; +import jakarta.json.bind.annotation.JsonbTypeSerializer; + +import static org.junit.Assert.assertEquals; + +/** + * Adapter on + * - Field + * - Constructor + * - Setter + * - Config + * - Class + * + * Still has a getter + * + * Outcome + * - Setter wins on read + * - Field wins on write + * - Constructor adapter is called, but overwritten + */ +public class SerializerPrecedenceConfigClassSetterFieldConstructorHasGetterTest extends SerializerOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig() + .withSerializers(new Adapter.Config()) + .withDeserializers(new Adapter.Config()) + ); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Setter.deserialize}", actual.toString()); + assertEquals("Constructor.deserialize\n" + + "Contact.\n" + + "Setter.deserialize\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"Field.serialize\"}}", json); + assertEquals("Contact.getEmail\n" + + "Field.serialize", calls()); + } + + public static class Contact { + + @JsonbTypeDeserializer(Adapter.Field.class) + @JsonbTypeSerializer(Adapter.Field.class) + private Email email; + + @JsonbCreator + public Contact(@JsonbProperty("email") @JsonbTypeDeserializer(Adapter.Constructor.class) final Email email) { + CALLS.called(); + this.email = email; + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + @JsonbTypeDeserializer(Adapter.Setter.class) + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassTest.java new file mode 100644 index 00000000..768c673e --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/symmetry/serializer/SerializerPrecedenceConfigClassTest.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.johnzon.jsonb.symmetry.serializer; + +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; + +import static org.junit.Assert.assertEquals; + +/** + * Adapter on + * - Config + * - Class + * + * Has + * - Getter + * - Final Field + * + * Outcome: + * - Config wins on read + * - Config wins on write + * + * Inconsistency: + * - Equivalent test for JsonbTypeAdapter the EmailClass adapter wins (likely bug in JsonbTypeAdapter code) + */ +public class SerializerPrecedenceConfigClassTest extends SerializerOnClassTest { + + @Override + public Jsonb jsonb() { + return JsonbBuilder.create(new JsonbConfig() + .withSerializers(new Adapter.Config()) + .withDeserializers(new Adapter.Config()) + ); + } + + @Override + public void assertRead(final Jsonb jsonb) { + final String json = "{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\"}}"; + final Contact actual = jsonb.fromJson(json, Contact.class); + assertEquals("Contact{email=test@domain.com:Config.deserialize}", actual.toString()); + assertEquals("Contact.\n" + + "Config.deserialize\n" + + "Contact.setEmail", calls()); + } + + @Override + public void assertWrite(final Jsonb jsonb) { + final Email email = new Email("test", "domain.com"); + final Contact contact = new Contact(); + contact.setEmail(email); + reset(); + + final String json = jsonb.toJson(contact); + assertEquals("{\"email\":{\"user\":\"test\",\"domain\":\"domain.com\",\"call\":\"Config.serialize\"}}", json); + assertEquals("Contact.getEmail\n" + + "Config.serialize", calls()); + } + + public static class Contact { + + private Email email; + + public Contact() { + CALLS.called(); + } + + public Email getEmail() { + CALLS.called(); + return email; + } + + public void setEmail(final Email email) { + CALLS.called(); + this.email = email; + } + + @Override + public String toString() { + return String.format("Contact{email=%s}", email); + } + } +}