diff --git a/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/JsonUnwrappedDeserializer.java b/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/JsonUnwrappedDeserializer.java index bddb4110225..9d7cf97782f 100644 --- a/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/JsonUnwrappedDeserializer.java +++ b/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/JsonUnwrappedDeserializer.java @@ -162,10 +162,11 @@ public T deserialize(JsonParser jsonParser, DeserializationContext deserializati boolean replaced = false; for (UnwrappedInfo unwrapped : unwrappedInfos) { - final ObjectNode unwrappedNode = unwrappedNodes.computeIfAbsent(unwrapped, - k -> deserializationContext.getNodeFactory().objectNode()); final String transformed = unwrapped.nameTransformer.reverse(key); + final ObjectNode unwrappedNode = unwrappedNodes.getOrDefault(unwrapped, + deserializationContext.getNodeFactory().objectNode()); if (transformed != null && !ownPropertyNames.contains(key) && unwrapped.beanPropertyNames.contains(transformed)) { + unwrappedNodes.putIfAbsent(unwrapped, unwrappedNode); unwrappedNode.replace(transformed, value); replaced = true; } diff --git a/kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson/JsonUnwrappedDeserializerTest.java b/kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson/JsonUnwrappedDeserializerTest.java index e08e8233470..c6cfc9ce730 100644 --- a/kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson/JsonUnwrappedDeserializerTest.java +++ b/kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson/JsonUnwrappedDeserializerTest.java @@ -23,6 +23,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import lombok.Data; import lombok.NoArgsConstructor; +import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -31,16 +32,9 @@ import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id.DEDUCTION; import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NONE; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertNotNull; class JsonUnwrappedDeserializerTest { - private static final String EXPECTED_VALUE_A = "Value A"; - private static final String EXPECTED_VALUE_B = "Value B"; - private static final String EXPECTED_VALUE_C = "Value C"; - private ObjectMapper mapper; @BeforeEach @@ -53,22 +47,39 @@ class Deserialize { @Test @DisplayName("Single @JsonUnwrapped polymorphic type") - void singleInterfaceWithJsonWrapped() throws JsonProcessingException { - RootClass instance = mapper.readValue("{ \"stringField\": \"" + EXPECTED_VALUE_A + "\", " - + "\"extendedField\": \"" + EXPECTED_VALUE_B + "\", " - + "\"nestedField\": \"" + EXPECTED_VALUE_C + "\" }", RootClass.class); - // Verify normal fields works along to the json-wrapped fields - assertEquals(EXPECTED_VALUE_A, instance.stringField); - - // Verify interfaces are supported at root level - assertNotNull(instance.rootInterface, "Interface was not deserialized!"); - assertInstanceOf(RootImplementation.class, instance.rootInterface); - RootImplementation rootImplementation = ((RootImplementation) instance.rootInterface); - assertEquals(EXPECTED_VALUE_B, rootImplementation.extendedField); - - // Verify nested interfaces are also supported - assertInstanceOf(NestedImplementation.class, rootImplementation.nestedInterface); - assertEquals(EXPECTED_VALUE_C, ((NestedImplementation) rootImplementation.nestedInterface).nestedField); + void singleJsonWrappedPolymorphicField() throws JsonProcessingException { + final RootClass result = mapper.readValue("{" + + "\"stringField\": \"string-field-value\", " + + "\"extendedField\": \"extended-field-value\", " + + "\"nestedField\": \"nested-field-value\"" + + "}", RootClass.class); + assertThat(result) + // Verify normal fields works along to the json-wrapped fields + .hasFieldOrPropertyWithValue("stringField", "string-field-value") + // Verify interfaces are supported at root level + .extracting(RootClass::getRootInterface) + .isNotNull() + .asInstanceOf(InstanceOfAssertFactories.type(RootImplementation.class)) + .hasFieldOrPropertyWithValue("extendedField", "extended-field-value") + // Verify nested interfaces are also supported + .extracting(RootImplementation::getNestedInterface) + .isNotNull() + .asInstanceOf(InstanceOfAssertFactories.type(NestedImplementation.class)) + .hasFieldOrPropertyWithValue("nestedField", "nested-field-value"); + } + + @Test + @DisplayName("Single @JsonUnwrapped polymorphic field with missing data") + void singleJsonWrappedPolymorphicFieldWithMissingDataForUnwrapped() throws JsonProcessingException { + final RootClass result = mapper.readValue("{" + + "\"stringField\": \"string-field-value\"" + + "}", RootClass.class); + assertThat(result) + // Verify normal fields works along to the json-wrapped fields + .hasFieldOrPropertyWithValue("stringField", "string-field-value") + // Verify interfaces are supported at root level + .extracting(RootClass::getRootInterface) + .isNull(); } @Test @@ -167,6 +178,8 @@ public static class BarImpl implements Bar { private String bar; } + @Data + @NoArgsConstructor @JsonDeserialize(using = io.fabric8.kubernetes.model.jackson.JsonUnwrappedDeserializer.class) public static class RootClass { @@ -175,25 +188,6 @@ public static class RootClass { @JsonUnwrapped private RootInterface rootInterface; - public RootClass() { - - } - - public String getStringField() { - return stringField; - } - - public void setStringField(String stringField) { - this.stringField = stringField; - } - - public RootInterface getRootInterface() { - return rootInterface; - } - - public void setRootInterface(RootInterface rootInterface) { - this.rootInterface = rootInterface; - } } @JsonSubTypes(@JsonSubTypes.Type(RootImplementation.class)) @@ -202,24 +196,14 @@ interface RootInterface { } + @Data + @NoArgsConstructor @JsonDeserialize(using = io.fabric8.kubernetes.model.jackson.JsonUnwrappedDeserializer.class) public static class RootImplementation implements RootInterface { private String extendedField; @JsonUnwrapped private NestedInterface nestedInterface; - - public RootImplementation() { - - } - - public String getExtendedField() { - return extendedField; - } - - public void setExtendedField(String extendedField) { - this.extendedField = extendedField; - } } @JsonSubTypes(@JsonSubTypes.Type(NestedImplementation.class)) @@ -228,19 +212,9 @@ interface NestedInterface { } + @Data + @NoArgsConstructor public static class NestedImplementation implements NestedInterface { private String nestedField; - - public NestedImplementation() { - - } - - public String getNestedField() { - return nestedField; - } - - public void setNestedField(String nestedField) { - this.nestedField = nestedField; - } } }