Skip to content

Commit

Permalink
Avoid duplicate deserialization of nested polymorphic objects (eclips…
Browse files Browse the repository at this point in the history
  • Loading branch information
greek1979 committed Feb 24, 2023
1 parent a4d7749 commit f44a65b
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
import java.util.ArrayDeque;
import java.util.Deque;

import org.eclipse.yasson.internal.properties.MessageKeys;
import org.eclipse.yasson.internal.properties.Messages;

import jakarta.json.JsonArray;
import jakarta.json.JsonNumber;
import jakarta.json.JsonObject;
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright (c) 2021, 2022 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
* <p>(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
* <p>(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 {

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,17 @@

import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;

import org.eclipse.yasson.adapters.AdaptersTest.StringAdapter;

import static org.eclipse.yasson.Jsonbs.*;

import jakarta.json.bind.Jsonb;
import jakarta.json.bind.JsonbBuilder;
import jakarta.json.bind.JsonbConfig;
import jakarta.json.bind.JsonbException;
import jakarta.json.bind.annotation.JsonbTypeInfo;
import jakarta.json.bind.annotation.JsonbSubtype;

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 {
Expand All @@ -50,33 +42,4 @@ public void setKey(String key) {
this.key = key;
}
}



/**
* Test for: https://github.com/eclipse-ee4j/yasson/issues/589
*/
@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;
}
}

0 comments on commit f44a65b

Please sign in to comment.