From f6267dae3ae76276ac7b2f2284b8380238270438 Mon Sep 17 00:00:00 2001 From: Rob Vesse Date: Wed, 8 Jul 2026 13:35:25 +0100 Subject: [PATCH] Expose non-pretty printed XML generation BomXmlGenerator has the ability to generate non pretty printed XML on a package private method but this was not exposed on the public API as it was for BomJsonGenerator. The non pretty printed code path for JSON generation was not actually exercised in tests so modified some existing generator tests to verify that non pretty printed output is smaller. When modifying the equivalent XML generation tests found that the equivalent test case wasn't actually exercising XML generation so fixed that which exposed a bug in how the XML generator serialized properties which was leading to JsonMappingException's from Jackson. That bug is also fixed with this commit. Signed-off-by: Rob Vesse --- .../generators/xml/BomXmlGenerator.java | 4 ++++ .../util/serializer/PropertiesSerializer.java | 6 ++--- .../util/serializer/SerializerUtils.java | 5 +++- .../org/cyclonedx/BomJsonGeneratorTest.java | 8 +++++++ .../org/cyclonedx/BomXmlGeneratorTest.java | 24 ++++++++++++------- src/test/resources/bom-1.5.json | 6 ++++- src/test/resources/bom-1.5.xml | 1 + 7 files changed, 41 insertions(+), 13 deletions(-) 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