diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/ObjectDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/ObjectDeserializer.java index acd62edc2..fa7d914ac 100644 --- a/src/main/java/org/eclipse/yasson/internal/deserializer/ObjectDeserializer.java +++ b/src/main/java/org/eclipse/yasson/internal/deserializer/ObjectDeserializer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureToParserAdapter.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureToParserAdapter.java index 94d378215..dfa9d64f4 100644 --- a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureToParserAdapter.java +++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureToParserAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -25,6 +25,9 @@ import jakarta.json.stream.JsonLocation; import jakarta.json.stream.JsonParser; +import org.eclipse.yasson.internal.properties.MessageKeys; +import org.eclipse.yasson.internal.properties.Messages; + /** * Adapter for {@link JsonParser}, that reads a {@link JsonStructure} content tree instead of JSON text. * @@ -66,9 +69,9 @@ public Event next() { JsonStructureIterator current = iterators.peek(); Event next = current.next(); if (next == Event.START_OBJECT) { - iterators.push(new JsonObjectIterator((JsonObject) iterators.peek().getValue())); + iterators.push(new JsonObjectIterator((JsonObject) current.getValue())); } else if (next == Event.START_ARRAY) { - iterators.push(new JsonArrayIterator((JsonArray) iterators.peek().getValue())); + iterators.push(new JsonArrayIterator((JsonArray) current.getValue())); } else if (next == Event.END_OBJECT || next == Event.END_ARRAY) { iterators.pop(); } @@ -102,8 +105,14 @@ public BigDecimal getBigDecimal() { @Override public JsonObject getObject() { -// ((JsonObjectIterator) iterators.peek()).jsonObject - return iterators.peek().getValue().asJsonObject(); + JsonStructureIterator current = iterators.peek(); + if (current instanceof JsonObjectIterator) { + //Remove child iterator as getObject() method contract says + iterators.pop(); + return current.getValue().asJsonObject(); + } else { + throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Outside of object context")); + } } private JsonNumber getJsonNumberValue() { @@ -120,6 +129,26 @@ public JsonLocation getLocation() { throw new JsonbException("Operation not supported"); } + @Override + public void skipArray() { + if (!iterators.isEmpty()) { + JsonStructureIterator current = iterators.peek(); + if (current instanceof JsonArrayIterator) { + iterators.pop(); + } + } + } + + @Override + public void skipObject() { + if (!iterators.isEmpty()) { + JsonStructureIterator current = iterators.peek(); + if (current instanceof JsonObjectIterator) { + iterators.pop(); + } + } + } + @Override public void close() { //noop diff --git a/src/main/java/org/eclipse/yasson/internal/model/JsonbAnnotatedElement.java b/src/main/java/org/eclipse/yasson/internal/model/JsonbAnnotatedElement.java index 774be03b2..b93abe986 100644 --- a/src/main/java/org/eclipse/yasson/internal/model/JsonbAnnotatedElement.java +++ b/src/main/java/org/eclipse/yasson/internal/model/JsonbAnnotatedElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -100,7 +100,7 @@ public void putAnnotation(Annotation annotation, boolean inherited, Class def // } // annotations.put(annotation.annotationType(), new AnnotationWrapper(annotation, inherited)); annotations.computeIfAbsent(annotation.annotationType(), aClass -> new LinkedList<>()) - .add(new AnnotationWrapper(annotation, inherited, definedType)); + .add(new AnnotationWrapper(annotation, inherited, definedType)); } public void putAnnotationWrapper(AnnotationWrapper annotationWrapper) { diff --git a/src/test/java/org/eclipse/yasson/customization/polymorphism/NestedPolymorphismTest.java b/src/test/java/org/eclipse/yasson/customization/polymorphism/NestedPolymorphismTest.java new file mode 100644 index 000000000..fe1c4dd1c --- /dev/null +++ b/src/test/java/org/eclipse/yasson/customization/polymorphism/NestedPolymorphismTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +package org.eclipse.yasson.customization.polymorphism; + +import jakarta.json.bind.annotation.JsonbSubtype; +import jakarta.json.bind.annotation.JsonbTypeInfo; + +import org.eclipse.yasson.Jsonbs; +import org.junit.jupiter.api.Test; + +import static org.eclipse.yasson.Jsonbs.defaultJsonb; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * Tests for verification of proper polymorphism handling based on annotation. + */ +public class NestedPolymorphismTest { + + /** + * 1st test for: https://github.com/eclipse-ee4j/yasson/issues/589 + *

(deserialization of nested polymorphic and unmapped properties) + */ + @Test + public void testNestedUnmappedProperty() { + String json = "{\"inner\":{\"id\":123,\"@type\":\"derivationA\"," + + "\"unmapped\":{\"x\":9,\"y\":[9,8,7]},\"name\":\"abc\"}}"; + Outer obj = assertDoesNotThrow(() -> defaultJsonb.fromJson(json, Outer.class)); + assertEquals(123L, obj.inner.id); + assertEquals("abc", obj.inner.name); + } + + // a base class + @JsonbTypeInfo(key = "@type", value = + @JsonbSubtype(type = InnerBase.class, alias = "derivationA")) + public static class InnerBase { + public Long id; + public String name; + } + + // derivation of the base class + public class Derivation extends InnerBase {} + + // an arbitrary 'outer' root element + public static class Outer { + public InnerBase inner; + } + + /** + * 2nd test for: https://github.com/eclipse-ee4j/yasson/issues/589 + *

(deserialization of multiple nested polymorphic properties) + */ + @Test + public void testNestedDeserialization() { + String json = "{\"@type\":\"Pets\",\"pet1\":{\"@type\":\"Cat\",\"name\":\"kitty\"}" + + ",\"pet2\":{\"@type\":\"Dog\",\"name\":\"puppy\"}}"; + final Animals animals = Jsonbs.defaultJsonb.fromJson(json, Animals.class); + assertThat(animals, instanceOf(Pets.class)); + assertNotNull(((Pets) animals).pet1, "Empty 'pet1' property"); + assertEquals("kitty", ((Cat) ((Pets) animals).pet1).name, "First pet has invalid name"); + assertNotNull(((Pets) animals).pet2, "Empty 'pet2' property"); + assertThat("Invalid pet nr 2", ((Pets) animals).pet2, instanceOf(Dog.class)); + } + + @JsonbTypeInfo(key = "@type", value = { + @JsonbSubtype(alias = "Dog", type = Dog.class), + @JsonbSubtype(alias = "Cat", type = Cat.class) + }) + public interface Pet { + public String getType(); + } + + public static class Dog implements Pet { + + public String name; + + @Override + public String getType() { + return "Dog"; + } + } + + public static class Cat implements Pet { + + public String name; + + @Override + public String getType() { + return "Cat"; + } + } + + @JsonbTypeInfo(key = "@type", value = { + @JsonbSubtype(alias = "Pets", type = Pets.class), + @JsonbSubtype(alias = "Fishes", type = Fishes.class) + }) + public interface Animals { + + } + + public static class Pets implements Animals { + public Pet pet1; + public Pet pet2; + } + + public static class Fishes implements Animals { + + } + +} diff --git a/src/test/java/org/eclipse/yasson/internal/serializer/ObjectDeserializerTest.java b/src/test/java/org/eclipse/yasson/internal/serializer/ObjectDeserializerTest.java index ad2f0d04b..3c4158159 100644 --- a/src/test/java/org/eclipse/yasson/internal/serializer/ObjectDeserializerTest.java +++ b/src/test/java/org/eclipse/yasson/internal/serializer/ObjectDeserializerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -22,9 +22,9 @@ public class ObjectDeserializerTest { @Test public void testGetInstanceExceptionShouldContainClassNameOnMissingConstructor() { - assertThrows(JsonbException.class, - () -> defaultJsonb.fromJson("{\"key\":\"value\"}", DummyDeserializationClass.class), - DummyDeserializationClass.class::getName); + assertThrows(JsonbException.class, + () -> defaultJsonb.fromJson("{\"key\":\"value\"}", DummyDeserializationClass.class), + DummyDeserializationClass.class::getName); } public static class DummyDeserializationClass {