Repository: CycloneDX/cyclonedx-core-java (java)

Issue:
XML deserialization fails for `metadata>component>properties`
Deserializing a BOM in XML format fails, when it contains a metadata component that has properties defined.

The following BOM can be used to reproduce the issue:

```xml
<?xml version="1.0" encoding="utf-8"?>
<bom xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
     serialNumber="urn:uuid:1624fa6f-aebe-4dba-8ead-f2c876c9b832" version="1"
     xmlns="http://cyclonedx.org/schema/bom/1.4">
    <metadata>
        <component type="application">
            <name>acme-app</name>
            <properties>
                <property name="foo">bar</property>
            </properties>
        </component>
    </metadata>
</bom>
```

Stacktrace:

```
com.fasterxml.jackson.databind.JsonMappingException: Cannot deserialize value of type `java.util.ArrayList<org.cyclonedx.model.Property>` from Object value (token `JsonToken.START_OBJECT`)
 at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: org.cyclonedx.model.Component["properties"]) (through reference chain: org.cyclonedx.model.Bom["metadata"])
	at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:402)
	at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:361)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1853)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:316)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
	at com.fasterxml.jackson.dataformat.xml.deser.WrapperHandlingDeserializer.deserialize(WrapperHandlingDeserializer.java:122)
	at com.fasterxml.jackson.dataformat.xml.deser.XmlDeserializationContext.readRootValue(XmlDeserializationContext.java:91)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4825)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3833)
	at org.cyclonedx.parsers.XmlParser.parse(XmlParser.java:87)
```

Pull request:
Fix `metadata>component>properties` XML deserialization
Fixes #336

Patch:
diff --git a/src/main/java/org/cyclonedx/model/Component.java b/src/main/java/org/cyclonedx/model/Component.java
index 2fe1af970..7ae2df3ae 100644
--- a/src/main/java/org/cyclonedx/model/Component.java
+++ b/src/main/java/org/cyclonedx/model/Component.java
@@ -35,6 +35,7 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
 import com.github.packageurl.PackageURL;
+import org.cyclonedx.util.deserializer.PropertiesDeserializer;
 
 @SuppressWarnings("unused")
 @JacksonXmlRootElement(localName = "component")
@@ -364,6 +365,7 @@ public class Component extends ExtensibleElement {
 
     @JacksonXmlElementWrapper(localName = "properties")
     @JacksonXmlProperty(localName = "property")
+    @JsonDeserialize(using = PropertiesDeserializer.class)
     public List<Property> getProperties() {
         return properties;
     }


Test patch:
diff --git a/src/test/java/org/cyclonedx/parsers/JsonParserTest.java b/src/test/java/org/cyclonedx/parsers/JsonParserTest.java
index 790a36435..3c09525f8 100644
--- a/src/test/java/org/cyclonedx/parsers/JsonParserTest.java
+++ b/src/test/java/org/cyclonedx/parsers/JsonParserTest.java
@@ -186,6 +186,13 @@ public class JsonParserTest
         assertMetadata_validTools(bom.getMetadata());
     }
 
+    @Test
+    public void testIssue336Regression() throws Exception {
+        final Bom bom = getJsonBom("regression/issue336.json");
+        assertEquals("foo", bom.getMetadata().getComponent().getProperties().get(0).getName());
+        assertEquals("bar", bom.getMetadata().getComponent().getProperties().get(0).getValue());
+    }
+    
     @Test
     public void testIssue338RegressionWithSingleTool() throws Exception {
         final Bom bom = getJsonBom("regression/issue338-single-tool.json");
diff --git a/src/test/java/org/cyclonedx/parsers/XmlParserTest.java b/src/test/java/org/cyclonedx/parsers/XmlParserTest.java
index 845d2d48e..ccbcabbef 100644
--- a/src/test/java/org/cyclonedx/parsers/XmlParserTest.java
+++ b/src/test/java/org/cyclonedx/parsers/XmlParserTest.java
@@ -342,6 +342,13 @@ public class XmlParserTest
         assertNull(bom.getVulnerabilities());
     }
 
+    @Test
+    public void testIssue336Regression() throws Exception {
+        final Bom bom = getXmlBom("regression/issue336.xml");
+        assertEquals("foo", bom.getMetadata().getComponent().getProperties().get(0).getName());
+        assertEquals("bar", bom.getMetadata().getComponent().getProperties().get(0).getValue());
+    }
+    
     @Test
     public void testIssue338RegressionWithSingleTool() throws Exception {
         final Bom bom = getXmlBom("regression/issue338-single-tool.xml");
diff --git a/src/test/resources/regression/issue336.json b/src/test/resources/regression/issue336.json
new file mode 100644
index 000000000..05e4d151f
--- /dev/null
+++ b/src/test/resources/regression/issue336.json
@@ -0,0 +1,17 @@
+{
+  "bomFormat" : "CycloneDX",
+  "specVersion" : "1.4",
+  "serialNumber": "urn:uuid:1624fa6f-aebe-4dba-8ead-f2c876c9b832",
+  "version" : 1,
+  "metadata": {
+    "component": {
+      "name": "acme-app",
+      "properties": [
+        {
+          "name": "foo",
+          "value": "bar"
+        }
+      ]
+    }
+  }
+}
diff --git a/src/test/resources/regression/issue336.xml b/src/test/resources/regression/issue336.xml
new file mode 100644
index 000000000..9a3fae5cd
--- /dev/null
+++ b/src/test/resources/regression/issue336.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<bom xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+     serialNumber="urn:uuid:1624fa6f-aebe-4dba-8ead-f2c876c9b832" version="1"
+     xmlns="http://cyclonedx.org/schema/bom/1.4">
+    <metadata>
+        <component type="application">
+            <name>acme-app</name>
+            <properties>
+                <property name="foo">bar</property>
+            </properties>
+        </component>
+    </metadata>
+</bom>
\ No newline at end of file