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

Issue:
BOM serialisation fails for both JSON and XML
org.cyclonedx:cyclonedx-core-java:9.0.3

For XML:

javax.xml.parsers.ParserConfigurationException: org.cyclonedx.exception.GeneratorException: com.fasterxml.jackson.databind.JsonMappingException: Can not write a field name, expecting a value (through reference chain: org.cyclonedx.model.Bom["component"]->java.util.ArrayList[8]->org.cyclonedx.model.Component["purl"])

<img width="506" alt="Screenshot 2024-06-25 at 12 56 10 PM" src="https://github.com/CycloneDX/cyclonedx-core-java/assets/1503649/8b8574a9-3280-4131-a153-ff471543dab5">


For JSON, output of BomJsonGenerator::toJsonString just empty string.

can be reproduced here: https://github.com/CycloneDX/cyclonedx-gradle-plugin/pull/444 

Pull request description:
Handle serialization of empty LicenseChoice correctly
Closes #439 

Patch:
diff --git a/src/main/java/org/cyclonedx/util/serializer/LicenseChoiceSerializer.java b/src/main/java/org/cyclonedx/util/serializer/LicenseChoiceSerializer.java
index ef6a27658..0d1f5fe4f 100644
--- a/src/main/java/org/cyclonedx/util/serializer/LicenseChoiceSerializer.java
+++ b/src/main/java/org/cyclonedx/util/serializer/LicenseChoiceSerializer.java
@@ -120,6 +120,9 @@ public class LicenseChoiceSerializer
     }
     else if (lc.getExpression() != null) {
       serializeExpressionToXml(lc, toXmlGenerator);
+    } else {
+      toXmlGenerator.writeStartArray();
+      toXmlGenerator.writeEndArray();
     }
   }
 
@@ -155,7 +158,10 @@ public class LicenseChoiceSerializer
     else if (licenseChoice.getExpression() != null &&
         StringUtils.isNotBlank(licenseChoice.getExpression().getValue())) {
       serializeExpressionToJson(licenseChoice, gen);
-    }
+    } else {
+      gen.writeStartArray();
+      gen.writeEndArray();
+  }
   }
 
   private void serializeExpressionToXml(


Test patch:
diff --git a/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java b/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java
index 67c53b01e..10fac4626 100644
--- a/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java
+++ b/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java
@@ -19,6 +19,7 @@
 package org.cyclonedx;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import java.nio.charset.StandardCharsets;
 import org.apache.commons.io.IOUtils;
 import org.cyclonedx.generators.BomGeneratorFactory;
 import org.cyclonedx.generators.json.BomJsonGenerator;
@@ -248,6 +249,29 @@ public class BomJsonGeneratorTest {
         assertTrue(parser.isValid(loadedFile, version));
     }
 
+    @Test
+    public void testIssue439Regression_jsonEmptyLicense() throws Exception {
+        Version version = Version.VERSION_16;
+        Bom bom = new Bom();
+        bom.addComponent(getComponentWithEmptyLicenseChoice());
+
+        BomJsonGenerator generator = BomGeneratorFactory.createJson(version, bom);
+        String jsonString = generator.toJsonString();
+
+        assertFalse(jsonString.isEmpty());
+        JsonParser parser = new JsonParser();
+        assertTrue(parser.isValid(jsonString.getBytes(StandardCharsets.UTF_8)));
+    }
+
+    private static Component getComponentWithEmptyLicenseChoice() {
+        Component component = new Component();
+        component.setName("xalan");
+        component.setType(Component.Type.LIBRARY);
+        component.setLicenses(new LicenseChoice());
+        component.setPurl("pkg:maven/xalan/xalan@2.6.0?type=jar");
+        return component;
+    }
+
     @Test
     public void schema16_testEvidence() throws Exception {
         Version version = Version.VERSION_16;
diff --git a/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java b/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java
index f2fabeb7a..759486eff 100644
--- a/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java
+++ b/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java
@@ -18,6 +18,7 @@
  */
 package org.cyclonedx;
 
+import java.nio.charset.StandardCharsets;
 import org.apache.commons.io.IOUtils;
 import org.cyclonedx.exception.ParseException;
 import org.cyclonedx.generators.BomGeneratorFactory;
@@ -375,6 +376,29 @@ public class BomXmlGeneratorTest {
         assertTrue(parser.isValid(loadedFile, version));
     }
 
+    @Test
+    public void testIssue439Regression_xmlEmptyLicense() throws Exception {
+        Version version = Version.VERSION_16;
+        Bom bom = new Bom();
+        bom.addComponent(getComponentWithEmptyLicenseChoice());
+
+        BomXmlGenerator generator = BomGeneratorFactory.createXml(version, bom);
+        String xmlString = generator.toXmlString();
+
+        assertFalse(xmlString.isEmpty());
+        XmlParser parser = new XmlParser();
+        assertTrue(parser.isValid(xmlString.getBytes(StandardCharsets.UTF_8)));
+    }
+
+    private static Component getComponentWithEmptyLicenseChoice() {
+        Component component = new Component();
+        component.setName("xalan");
+        component.setType(Component.Type.LIBRARY);
+        component.setLicenses(new LicenseChoice());
+        component.setPurl("pkg:maven/xalan/xalan@2.6.0?type=jar");
+        return component;
+    }
+
     @Test
     public void schema16_testEvidence() throws Exception {
         Version version = Version.VERSION_16;