Repository: CycloneDX/cyclonedx-core-java
Pull request #81 (2021-03-05 15:19:11)

BOM JSON Parsing fails when license expression is used
Added logic to the LicenseDeserializer class to handle deserializing JSON components where the LicenseChoice is defined with an expression instead of individual license records.

This pull request addresses [Issue 80](https://github.com/CycloneDX/cyclonedx-core-java/issues/80)


Patch:
diff --git a/src/main/java/org/cyclonedx/util/LicenseDeserializer.java b/src/main/java/org/cyclonedx/util/LicenseDeserializer.java
index 79b97856e..347f5e189 100644
--- a/src/main/java/org/cyclonedx/util/LicenseDeserializer.java
+++ b/src/main/java/org/cyclonedx/util/LicenseDeserializer.java
@@ -44,7 +44,7 @@ public class LicenseDeserializer extends JsonDeserializer<LicenseChoice>
 
   @Override
   public LicenseChoice deserialize(
-      final JsonParser p, final DeserializationContext ctxt) throws IOException, JsonProcessingException
+    final JsonParser p, final DeserializationContext ctxt) throws IOException, JsonProcessingException
   {
     if (p instanceof FromXmlParser) {
       return p.readValueAs(LicenseChoice.class);
@@ -55,10 +55,19 @@ public class LicenseDeserializer extends JsonDeserializer<LicenseChoice>
     MapType type = factory.constructMapType(HashMap.class, String.class, License.class);
     LicenseChoice licenseChoice = new LicenseChoice();
     for (JsonNode node : nodes) {
-      HashMap<String, License> map = this.mapper.readValue(node.toString(), type);
-
-      if (map.get("license") != null) {
-        licenseChoice.addLicense(map.get("license"));
+      HashMap<String, License> map = null;
+      try
+      {
+        map = this.mapper.readValue(node.toString(), type);
+        if(map.get("license") != null)
+        {
+          licenseChoice.addLicense(map.get("license"));
+        }
+      }
+      catch(JsonProcessingException e)
+      {
+        // Check for expressions
+        licenseChoice = this.mapper.readValue(node.toString(), LicenseChoice.class);
       }
     }
 


Test patch:
diff --git a/src/test/java/org/cyclonedx/parsers/JsonParserTest.java b/src/test/java/org/cyclonedx/parsers/JsonParserTest.java
index 8ea3c1a4a..b37e72605 100644
--- a/src/test/java/org/cyclonedx/parsers/JsonParserTest.java
+++ b/src/test/java/org/cyclonedx/parsers/JsonParserTest.java
@@ -43,7 +43,7 @@ public class JsonParserTest {
         final JsonParser parser = new JsonParser();
         final Bom bom = parser.parse(bomBytes);
         Assert.assertEquals("1.2", bom.getSpecVersion());
-        Assert.assertEquals(2, bom.getComponents().size());
+        Assert.assertEquals(3, bom.getComponents().size());
         Assert.assertEquals(1, bom.getVersion());
         Assert.assertNotNull(bom.getMetadata());
         Assert.assertNotNull(bom.getMetadata().getTimestamp());
@@ -77,8 +77,9 @@ public class JsonParserTest {
         Assert.assertEquals(1, bom.getMetadata().getSupplier().getContacts().size());
         Assert.assertEquals("Acme Distribution", bom.getMetadata().getSupplier().getContacts().get(0).getName());
         Assert.assertEquals("distribution@example.com", bom.getMetadata().getSupplier().getContacts().get(0).getEmail());
+
         final List<Component> components = bom.getComponents();
-        Assert.assertEquals(2, components.size());
+        Assert.assertEquals(3, components.size());
         Component c1 = components.get(0);
         Assert.assertEquals("com.acme", c1.getGroup());
         Assert.assertEquals("tomcat-catalina", c1.getName());
@@ -86,6 +87,15 @@ public class JsonParserTest {
         Assert.assertEquals(Component.Type.LIBRARY, c1.getType());
         Assert.assertEquals("pkg:npm/acme/component@1.0.0", c1.getPurl());
 
+        Component c3 = components.get(2);
+        Assert.assertEquals(Component.Type.LIBRARY, c3.getType());
+        Assert.assertEquals("pkg:maven/org.glassfish.hk2/osgi-resource-locator@1.0.1?type=jar", c3.getBomRef());
+        Assert.assertEquals("GlassFish Community", c3.getPublisher());
+        Assert.assertEquals("org.glassfish.hk2", c3.getGroup());
+        Assert.assertEquals("osgi-resource-locator", c3.getName());
+        Assert.assertEquals("1.0.1", c3.getVersion());
+        Assert.assertEquals( "(CDDL-1.0 OR GPL-2.0-with-classpath-exception)", c3.getLicenseChoice().getExpression());
+
         testPedigree(c1);
 
         // Begin Services
diff --git a/src/test/resources/bom-1.2.json b/src/test/resources/bom-1.2.json
index e188a57b2..6356e8dd2 100644
--- a/src/test/resources/bom-1.2.json
+++ b/src/test/resources/bom-1.2.json
@@ -196,6 +196,15 @@
       "group": "org.example",
       "name": "mylibrary",
       "version": "1.0.0"
+    },
+    {
+      "type": "library",
+      "bom-ref": "pkg:maven/org.glassfish.hk2/osgi-resource-locator@1.0.1?type=jar",
+      "publisher": "GlassFish Community",
+      "group": "org.glassfish.hk2",
+      "name": "osgi-resource-locator",
+      "version": "1.0.1",
+      "licenses": [{"expression": "(CDDL-1.0 OR GPL-2.0-with-classpath-exception)"}]
     }
   ],
   "services": [