diff --git a/src/main/java/org/cyclonedx/generators/xml/BomXmlGenerator.java b/src/main/java/org/cyclonedx/generators/xml/BomXmlGenerator.java index bc0f29beb2..b985c8f237 100644 --- a/src/main/java/org/cyclonedx/generators/xml/BomXmlGenerator.java +++ b/src/main/java/org/cyclonedx/generators/xml/BomXmlGenerator.java @@ -133,6 +133,10 @@ public String toXmlString() throws GeneratorException { return toXML(bom, true); } + public String toXmlString(boolean prettyPrint) throws GeneratorException { + return toXML(bom, prettyPrint); + } + /** * Creates a text representation of a CycloneDX BoM Document. This method calls {@link #toXmlString()} and will return * an empty string if {@link #toXmlString()} throws an exception. It's preferred to call {@link #toXmlString()} diff --git a/src/main/java/org/cyclonedx/util/serializer/PropertiesSerializer.java b/src/main/java/org/cyclonedx/util/serializer/PropertiesSerializer.java index d4eaa9b770..830a79be70 100644 --- a/src/main/java/org/cyclonedx/util/serializer/PropertiesSerializer.java +++ b/src/main/java/org/cyclonedx/util/serializer/PropertiesSerializer.java @@ -53,11 +53,11 @@ private void serializerJson(List properties, JsonGenerator jsonGenerat private static void serializeXml(final List properties, final ToXmlGenerator xmlGenerator) throws IOException { - xmlGenerator.writeStartArray(); + // NB - In this context Jackson already knows we're producing elements so no need to explicitly try and + // write the property field name otherwise we end up with malformed XML with double wrapped elements for (Property property : properties) { - SerializerUtils.serializeProperty("property", property, xmlGenerator); + SerializerUtils.serializeProperty(null, property, xmlGenerator); } - xmlGenerator.writeEndArray(); } @Override diff --git a/src/main/java/org/cyclonedx/util/serializer/SerializerUtils.java b/src/main/java/org/cyclonedx/util/serializer/SerializerUtils.java index 8158d5cb32..6f4353d142 100644 --- a/src/main/java/org/cyclonedx/util/serializer/SerializerUtils.java +++ b/src/main/java/org/cyclonedx/util/serializer/SerializerUtils.java @@ -9,6 +9,7 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator; +import org.apache.commons.lang3.StringUtils; import org.cyclonedx.Version; import org.cyclonedx.model.ExternalReference; import org.cyclonedx.model.Hash; @@ -141,7 +142,9 @@ public static List filterHashesByVersion(List hashes, Version versio } public static void serializeProperty(String propertyName, Property prop, ToXmlGenerator xmlGenerator) throws IOException { - xmlGenerator.writeFieldName(propertyName); + if (StringUtils.isNotBlank(propertyName)) { + xmlGenerator.writeFieldName(propertyName); + } xmlGenerator.writeStartObject(); xmlGenerator.setNextIsAttribute(true); xmlGenerator.writeFieldName("name"); diff --git a/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java b/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java index 77310c3cd1..d698c6bbb8 100644 --- a/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java +++ b/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java @@ -145,6 +145,7 @@ public void schema13MultipleDependenciesJsonTest() throws Exception { static Stream testData() { return Stream.of( + Arguments.of(Version.VERSION_17, "/1.7/valid-bom-1.7.xml"), Arguments.of(Version.VERSION_16, "/1.6/valid-bom-1.6.xml"), Arguments.of(Version.VERSION_15, "/bom-1.5.xml"), Arguments.of(Version.VERSION_14, "/bom-1.4.xml"), @@ -165,6 +166,13 @@ public void testJsonGeneration(Version version, String bomXmlPath) File file = writeToFile(generator.toJsonString()); JsonParser parser = new JsonParser(); assertTrue(parser.isValid(file, version)); + + // Verify that with pretty printing disabled the output JSON is smaller + long prettyPrintedSize = file.length(); + file = writeToFile(generator.toJsonString(false)); + assertTrue(parser.isValid(file, version)); + assertTrue(file.length() < prettyPrintedSize, + "Non pretty-printed output size should be smaller than pretty printed output size"); } @Test diff --git a/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java b/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java index 406f5d44c1..76c8d9d9b2 100644 --- a/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java +++ b/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java @@ -182,6 +182,7 @@ public void schema12GenerationWithPedigreeDataTest() throws Exception { static Stream testData() { return Stream.of( + Arguments.of(Version.VERSION_17, "/1.7/valid-bom-1.7.json"), Arguments.of(Version.VERSION_16, "/1.6/valid-bom-1.6.json"), Arguments.of(Version.VERSION_15, "/bom-1.5.json"), Arguments.of(Version.VERSION_14, "/bom-1.4.json"), @@ -191,17 +192,24 @@ static Stream testData() { @ParameterizedTest @MethodSource("testData") - public void testXmlGeneration(Version version, String bomXmlPath) + public void testXmlGeneration(Version version, String bomJsonPath) throws Exception { - Bom bom = createCommonJsonBom(bomXmlPath); - BomJsonGenerator generator = BomGeneratorFactory.createJson(version, bom); + Bom bom = createCommonJsonBom(bomJsonPath); + BomXmlGenerator generator = BomGeneratorFactory.createXml(version, bom); assertEquals(version, generator.getSchemaVersion()); - File file = writeToFile(generator.toJsonString()); - JsonParser parser = new JsonParser(); + File file = writeToFile(generator.toXmlString()); + XmlParser parser = new XmlParser(); + assertTrue(parser.isValid(file, version)); + + // Verify that with pretty printing disabled the output XML is smaller + long prettyPrintedSize = file.length(); + file = writeToFile(generator.toXmlString(false)); assertTrue(parser.isValid(file, version)); + assertTrue(file.length() < prettyPrintedSize, + "Non pretty-printed output size should be smaller than pretty printed output size"); } @Test @@ -825,10 +833,10 @@ public void testIssue571() throws Exception { component.setType(Type.APPLICATION); bom.getMetadata().getToolChoice().getComponents().add(component); - BomJsonGenerator generator = BomGeneratorFactory.createJson(version, bom); - File loadedFile = writeToFile(generator.toJsonString()); + BomXmlGenerator generator = BomGeneratorFactory.createXml(version, bom); + File loadedFile = writeToFile(generator.toXmlString()); - JsonParser parser = new JsonParser(); + XmlParser parser = new XmlParser(); assertTrue(parser.isValid(loadedFile, version)); } diff --git a/src/test/resources/bom-1.5.json b/src/test/resources/bom-1.5.json index 87d6ddb43e..33937c5b82 100644 --- a/src/test/resources/bom-1.5.json +++ b/src/test/resources/bom-1.5.json @@ -905,7 +905,7 @@ "timeEnd": "2023-01-01T00:00:00+10:00", "workspaces": [ { - "bom-ref": "workspace-1", + "bom-ref": "workspace-2", "uid": "workspace-1", "name": "My workspace", "aliases": [ "default-workspace" ], @@ -942,6 +942,10 @@ { "name": "Foo", "value": "Bar" + }, + { + "name": "Another", + "value": "Bar" } ] } diff --git a/src/test/resources/bom-1.5.xml b/src/test/resources/bom-1.5.xml index 6f06d98288..2109b817cc 100644 --- a/src/test/resources/bom-1.5.xml +++ b/src/test/resources/bom-1.5.xml @@ -737,6 +737,7 @@ Bar + Bar