Repository: CycloneDX/cyclonedx-core-java
Pull request #431 (2024-06-18 13:31:10)

Fix order of elements for generated SBOMs to match the specs
This is one of the last PRs to improve code and future testing 


Patch:
diff --git a/src/main/java/org/cyclonedx/model/AttachmentText.java b/src/main/java/org/cyclonedx/model/AttachmentText.java
index ee8fa39b8..647941f99 100644
--- a/src/main/java/org/cyclonedx/model/AttachmentText.java
+++ b/src/main/java/org/cyclonedx/model/AttachmentText.java
@@ -31,6 +31,7 @@ import org.cyclonedx.util.deserializer.AttachmentTextDeserializer;
 
 @SuppressWarnings("unused")
 @JsonPropertyOrder({
+    "contentType",
     "content-type",
     "encoding"})
 @JsonInclude(JsonInclude.Include.NON_NULL)
diff --git a/src/main/java/org/cyclonedx/model/Bom.java b/src/main/java/org/cyclonedx/model/Bom.java
index f9a7b0ae5..57327c8b6 100644
--- a/src/main/java/org/cyclonedx/model/Bom.java
+++ b/src/main/java/org/cyclonedx/model/Bom.java
@@ -107,6 +107,7 @@ public class Bom extends ExtensibleElement {
     private int version = 1;
 
     @JacksonXmlProperty(isAttribute = true)
+    @VersionFilter(Version.VERSION_11)
     private String serialNumber;
 
     @JsonOnly
diff --git a/src/main/java/org/cyclonedx/model/Component.java b/src/main/java/org/cyclonedx/model/Component.java
index b08ade31a..0c4b95f31 100644
--- a/src/main/java/org/cyclonedx/model/Component.java
+++ b/src/main/java/org/cyclonedx/model/Component.java
@@ -48,8 +48,12 @@ import org.cyclonedx.util.deserializer.PropertiesDeserializer;
 @JsonIgnoreProperties(ignoreUnknown = true)
 @JsonInclude(JsonInclude.Include.NON_EMPTY)
 @JsonPropertyOrder(
-    {"supplier",
+    {
+     "type",
+     "bom-ref",
+     "supplier",
      "manufacturer",
+     "authors",
      "author",
      "publisher",
      "group",
@@ -148,6 +152,9 @@ public class Component extends ExtensibleElement {
     @JsonProperty("mime-type")
     private String mimeType;
 
+    @JacksonXmlProperty(isAttribute = true)
+    private Type type;
+
     @VersionFilter(Version.VERSION_12)
     private OrganizationalEntity supplier;
 
@@ -192,9 +199,6 @@ public class Component extends ExtensibleElement {
     @VersionFilter(Version.VERSION_13)
     private Evidence evidence;
 
-    @JacksonXmlProperty(isAttribute = true)
-    private Type type;
-
     @VersionFilter(Version.VERSION_14)
     private ReleaseNotes releaseNotes;
 
@@ -505,6 +509,7 @@ public class Component extends ExtensibleElement {
     }
 
     @JacksonXmlElementWrapper(useWrapping = false)
+    @VersionFilter(Version.VERSION_16)
     public List<String> getOmniborId() {
         return omniborId;
     }
@@ -514,6 +519,7 @@ public class Component extends ExtensibleElement {
     }
 
     @JacksonXmlElementWrapper(useWrapping = false)
+    @VersionFilter(Version.VERSION_16)
     public List<String> getSwhid() {
         return swhid;
     }
diff --git a/src/main/java/org/cyclonedx/model/Issue.java b/src/main/java/org/cyclonedx/model/Issue.java
index 2b6b4305a..1ad19c8c3 100644
--- a/src/main/java/org/cyclonedx/model/Issue.java
+++ b/src/main/java/org/cyclonedx/model/Issue.java
@@ -31,7 +31,7 @@ import java.util.Objects;
 
 @JsonIgnoreProperties(ignoreUnknown = true)
 @JsonInclude(JsonInclude.Include.NON_EMPTY)
-@JsonPropertyOrder({"id", "name", "description", "source", "reference"})
+@JsonPropertyOrder({"id", "name", "description", "source", "references"})
 public class Issue {
 
   public enum Type {
diff --git a/src/main/java/org/cyclonedx/model/License.java b/src/main/java/org/cyclonedx/model/License.java
index 1e8a074df..3cdffd438 100644
--- a/src/main/java/org/cyclonedx/model/License.java
+++ b/src/main/java/org/cyclonedx/model/License.java
@@ -35,7 +35,7 @@ import org.cyclonedx.util.deserializer.PropertiesDeserializer;
 @SuppressWarnings("unused")
 @JsonIgnoreProperties(ignoreUnknown = true)
 @JsonInclude(Include.NON_EMPTY)
-@JsonPropertyOrder({"id", "name", "acknowledgement", "licensing", "text", "url", "properties"})
+@JsonPropertyOrder({"bom-ref", "id", "name", "acknowledgement", "licensing", "text", "url", "properties"})
 @JsonRootName("license")
 public class License extends ExtensibleElement {
 
diff --git a/src/main/java/org/cyclonedx/model/Metadata.java b/src/main/java/org/cyclonedx/model/Metadata.java
index 3b8c64fac..4d57fe919 100644
--- a/src/main/java/org/cyclonedx/model/Metadata.java
+++ b/src/main/java/org/cyclonedx/model/Metadata.java
@@ -41,7 +41,8 @@ import java.util.Objects;
 @JsonIgnoreProperties(ignoreUnknown = true)
 @JsonInclude(JsonInclude.Include.NON_EMPTY)
 @JsonPropertyOrder({
-    "timestamp", "lifecycles", "tools", "authors", "component", "manufacture", "supplier", "licenses", "properties"
+    "timestamp", "lifecycles", "tools", "authors", "component", "manufacturer", "manufacture", "supplier", "licenses",
+    "properties"
 })
 @JsonDeserialize(using = MetadataDeserializer.class)
 public class Metadata
diff --git a/src/main/java/org/cyclonedx/model/Service.java b/src/main/java/org/cyclonedx/model/Service.java
index 02d020934..d52bc0043 100644
--- a/src/main/java/org/cyclonedx/model/Service.java
+++ b/src/main/java/org/cyclonedx/model/Service.java
@@ -41,6 +41,7 @@ import java.util.Objects;
 @JsonIgnoreProperties(ignoreUnknown = true)
 @JsonInclude(JsonInclude.Include.NON_EMPTY)
 @JsonPropertyOrder({
+        "bom-ref",
         "provider",
         "group",
         "name",
diff --git a/src/main/java/org/cyclonedx/model/ServiceData.java b/src/main/java/org/cyclonedx/model/ServiceData.java
index 8fbb4302f..66efe098e 100644
--- a/src/main/java/org/cyclonedx/model/ServiceData.java
+++ b/src/main/java/org/cyclonedx/model/ServiceData.java
@@ -22,6 +22,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;
 import java.util.Objects;
@@ -29,6 +30,10 @@ import java.util.Objects;
 @SuppressWarnings("unused")
 @JsonIgnoreProperties(ignoreUnknown = true)
 @JsonInclude(Include.NON_EMPTY)
+@JsonPropertyOrder({
+    "classification",
+    "flow"
+})
 public class ServiceData {
 
     public enum Flow {
diff --git a/src/main/java/org/cyclonedx/model/Swid.java b/src/main/java/org/cyclonedx/model/Swid.java
index 41083e92d..8ddc2a9a9 100644
--- a/src/main/java/org/cyclonedx/model/Swid.java
+++ b/src/main/java/org/cyclonedx/model/Swid.java
@@ -38,10 +38,10 @@ public class Swid {
     private String version;
 
     @JacksonXmlProperty(isAttribute = true)
-    private int tagVersion;
+    private Integer tagVersion;
 
     @JacksonXmlProperty(isAttribute = true)
-    private boolean patch;
+    private Boolean patch;
 
     @JacksonXmlProperty(localName = "text")
     @JsonProperty("text")
@@ -71,19 +71,19 @@ public class Swid {
         this.version = version;
     }
 
-    public int getTagVersion() {
+    public Integer getTagVersion() {
         return tagVersion;
     }
 
-    public void setTagVersion(int tagVersion) {
+    public void setTagVersion(Integer tagVersion) {
         this.tagVersion = tagVersion;
     }
 
-    public boolean isPatch() {
+    public Boolean isPatch() {
         return patch;
     }
 
-    public void setPatch(boolean patch) {
+    public void setPatch(Boolean patch) {
         this.patch = patch;
     }
 
diff --git a/src/main/java/org/cyclonedx/model/component/crypto/CipherSuite.java b/src/main/java/org/cyclonedx/model/component/crypto/CipherSuite.java
index fc1a11122..d66f0b5fb 100644
--- a/src/main/java/org/cyclonedx/model/component/crypto/CipherSuite.java
+++ b/src/main/java/org/cyclonedx/model/component/crypto/CipherSuite.java
@@ -6,6 +6,8 @@ import java.util.Objects;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
 
 @JsonIgnoreProperties(ignoreUnknown = true)
 @JsonInclude(JsonInclude.Include.NON_EMPTY)
@@ -27,6 +29,8 @@ public class CipherSuite
     this.name = name;
   }
 
+  @JacksonXmlElementWrapper(localName = "algorithms")
+  @JacksonXmlProperty(localName = "algorithm")
   public List<String> getAlgorithms() {
     return algorithms;
   }
@@ -35,6 +39,8 @@ public class CipherSuite
     this.algorithms = algorithms;
   }
 
+  @JacksonXmlElementWrapper(localName = "identifiers")
+  @JacksonXmlProperty(localName = "identifier")
   public List<String> getIdentifiers() {
     return identifiers;
   }
diff --git a/src/main/java/org/cyclonedx/model/component/crypto/ProtocolProperties.java b/src/main/java/org/cyclonedx/model/component/crypto/ProtocolProperties.java
index f0e4d63f7..4939f7c82 100644
--- a/src/main/java/org/cyclonedx/model/component/crypto/ProtocolProperties.java
+++ b/src/main/java/org/cyclonedx/model/component/crypto/ProtocolProperties.java
@@ -6,7 +6,10 @@ import java.util.Objects;
 
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
 import org.cyclonedx.model.component.crypto.enums.ProtocolType;
 
 @JsonIgnoreProperties(ignoreUnknown = true)
@@ -40,6 +43,9 @@ public class ProtocolProperties
     this.version = version;
   }
 
+  @JacksonXmlElementWrapper(localName = "cipherSuites")
+  @JacksonXmlProperty(localName = "cipherSuite")
+  @JsonProperty("cipherSuites")
   public List<CipherSuite> getCipherSuites() {
     return cipherSuites;
   }
diff --git a/src/main/java/org/cyclonedx/model/component/modelCard/consideration/EnvironmentalConsideration.java b/src/main/java/org/cyclonedx/model/component/modelCard/consideration/EnvironmentalConsideration.java
index bdba9c618..f4ebb0925 100644
--- a/src/main/java/org/cyclonedx/model/component/modelCard/consideration/EnvironmentalConsideration.java
+++ b/src/main/java/org/cyclonedx/model/component/modelCard/consideration/EnvironmentalConsideration.java
@@ -18,6 +18,8 @@ public class EnvironmentalConsideration
 
   private List<Property> properties;
 
+  @JacksonXmlElementWrapper(localName = "energyConsumptions")
+  @JacksonXmlProperty(localName = "energyConsumption")
   public List<EnergyConsumption> getEnergyConsumptions() {
     return energyConsumptions;
   }
diff --git a/src/main/java/org/cyclonedx/model/formulation/Formula.java b/src/main/java/org/cyclonedx/model/formulation/Formula.java
index 9053b68e7..dca57a8ab 100644
--- a/src/main/java/org/cyclonedx/model/formulation/Formula.java
+++ b/src/main/java/org/cyclonedx/model/formulation/Formula.java
@@ -36,7 +36,7 @@ import org.cyclonedx.model.Service;
 @JsonIgnoreProperties(ignoreUnknown = true)
 @JsonInclude(JsonInclude.Include.NON_EMPTY)
 @JsonPropertyOrder(
-    {"components", "services", "workflows", "properties"})
+    {"bom-ref", "components", "services", "workflows", "properties"})
 public class Formula
     extends ExtensibleElement
 {
diff --git a/src/main/java/org/cyclonedx/model/formulation/Workflow.java b/src/main/java/org/cyclonedx/model/formulation/Workflow.java
index 6d5975264..9f409052b 100644
--- a/src/main/java/org/cyclonedx/model/formulation/Workflow.java
+++ b/src/main/java/org/cyclonedx/model/formulation/Workflow.java
@@ -13,8 +13,8 @@ import org.cyclonedx.model.formulation.task.Task;
 @JsonInclude(JsonInclude.Include.NON_EMPTY)
 @JsonPropertyOrder(
     {
-        "uid", "name", "description", "resourceReferences", "tasks", "taskDependencies", "taskTypes", "trigger",
-        "steps", "inputs", "outputs", "timeStart", "timeEnd", "workspaces", "runtimeTopology", "properties"
+        "bom-ref", "uid", "name", "description", "resourceReferences", "tasks", "taskDependencies", "taskTypes",
+        "trigger", "steps", "inputs", "outputs", "timeStart", "timeEnd", "workspaces", "runtimeTopology", "properties"
     })
 public class Workflow extends FormulationCommon
 {
diff --git a/src/main/java/org/cyclonedx/model/formulation/task/Task.java b/src/main/java/org/cyclonedx/model/formulation/task/Task.java
index 6b4e59c9e..0f2042721 100644
--- a/src/main/java/org/cyclonedx/model/formulation/task/Task.java
+++ b/src/main/java/org/cyclonedx/model/formulation/task/Task.java
@@ -12,8 +12,8 @@ import org.cyclonedx.model.formulation.FormulationCommon;
 @JsonInclude(JsonInclude.Include.NON_EMPTY)
 @JsonPropertyOrder(
     {
-        "uid", "name", "description", "resourceReferences", "taskTypes", "trigger", "steps", "inputs", "outputs",
-        "timeStart", "timeEnd", "workspaces", "runtimeTopology", "taskDependencyGraph", "properties",
+        "bom-ref", "uid", "name", "description", "resourceReferences", "taskTypes", "trigger", "steps", "inputs",
+        "outputs", "timeStart", "timeEnd", "workspaces", "runtimeTopology", "taskDependencyGraph", "properties"
     })
 public class Task
     extends FormulationCommon
diff --git a/src/main/java/org/cyclonedx/model/formulation/trigger/Event.java b/src/main/java/org/cyclonedx/model/formulation/trigger/Event.java
index 680ed4b44..d481f128d 100644
--- a/src/main/java/org/cyclonedx/model/formulation/trigger/Event.java
+++ b/src/main/java/org/cyclonedx/model/formulation/trigger/Event.java
@@ -3,19 +3,36 @@ package org.cyclonedx.model.formulation.trigger;
 import java.util.List;
 import java.util.Objects;
 
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.annotation.JsonRootName;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
 import org.cyclonedx.model.AttachmentText;
 import org.cyclonedx.model.Property;
 import org.cyclonedx.model.formulation.common.ResourceReferenceChoice;
 
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(Include.NON_EMPTY)
+@JsonPropertyOrder({"uid", "description", "timeReceived", "data", "source", "target", "properties"})
+@JsonRootName("event")
 public class Event {
+
   private String uid;
+
   private String description;
+
   private String timeReceived;
+
   private AttachmentText data;
+
   private ResourceReferenceChoice source;
+
   private ResourceReferenceChoice target;
+
   private List<Property> properties;
 
   public String getUid() {
@@ -42,6 +59,8 @@ public class Event {
     this.timeReceived = timeReceived;
   }
 
+  @JacksonXmlProperty(localName = "data")
+  @JsonProperty("data")
   public AttachmentText getData() {
     return data;
   }
diff --git a/src/main/java/org/cyclonedx/model/formulation/trigger/Trigger.java b/src/main/java/org/cyclonedx/model/formulation/trigger/Trigger.java
index d4b103cb6..df093a2a3 100644
--- a/src/main/java/org/cyclonedx/model/formulation/trigger/Trigger.java
+++ b/src/main/java/org/cyclonedx/model/formulation/trigger/Trigger.java
@@ -14,8 +14,8 @@ import org.cyclonedx.model.formulation.common.OutputType;
 @JsonIgnoreProperties(ignoreUnknown = true)
 @JsonInclude(JsonInclude.Include.NON_EMPTY)
 @JsonPropertyOrder({
-    "uid", "name", "description", "resourceReferences", "type", "event", "conditions", "timeActivated", "inputs",
-    "outputs", "properties"
+    "bom-ref", "uid", "name", "description", "resourceReferences", "type", "event", "conditions", "timeActivated",
+    "inputs", "outputs", "properties"
 })
 public class Trigger
     extends BasicDataAbstract
diff --git a/src/main/java/org/cyclonedx/model/formulation/workspace/Workspace.java b/src/main/java/org/cyclonedx/model/formulation/workspace/Workspace.java
index 680f603eb..bd5ac1348 100644
--- a/src/main/java/org/cyclonedx/model/formulation/workspace/Workspace.java
+++ b/src/main/java/org/cyclonedx/model/formulation/workspace/Workspace.java
@@ -13,8 +13,8 @@ import org.cyclonedx.model.formulation.common.BasicDataAbstract;
 @JsonIgnoreProperties(ignoreUnknown = true)
 @JsonInclude(JsonInclude.Include.NON_EMPTY)
 @JsonPropertyOrder({
-    "uid", "name", "aliases", "description", "resourceReferences", "accessMode", "mountPath", "managedDataType",
-    "volumeRequest", "volume", "properties"
+    "bom-ref", "uid", "name", "aliases", "description", "resourceReferences", "accessMode", "mountPath",
+    "managedDataType", "volumeRequest", "volume", "properties"
 })
 public class Workspace extends BasicDataAbstract
 {
diff --git a/src/main/java/org/cyclonedx/util/serializer/HashSerializer.java b/src/main/java/org/cyclonedx/util/serializer/HashSerializer.java
index f8d64a055..362984337 100644
--- a/src/main/java/org/cyclonedx/util/serializer/HashSerializer.java
+++ b/src/main/java/org/cyclonedx/util/serializer/HashSerializer.java
@@ -1,3 +1,21 @@
+/*
+ * This file is part of CycloneDX Core (Java).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright (c) OWASP Foundation. All Rights Reserved.
+ */
 package org.cyclonedx.util.serializer;
 
 import java.io.IOException;
@@ -79,4 +97,4 @@ public class HashSerializer
       return false;
     }
   }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/cyclonedx/util/serializer/MetadataSerializer.java b/src/main/java/org/cyclonedx/util/serializer/MetadataSerializer.java
index d0aa9e876..b688fdf9e 100644
--- a/src/main/java/org/cyclonedx/util/serializer/MetadataSerializer.java
+++ b/src/main/java/org/cyclonedx/util/serializer/MetadataSerializer.java
@@ -34,16 +34,21 @@ public class MetadataSerializer
 
   @Override
   public void serialize(Metadata output, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
-      throws IOException {
+      throws IOException
+  {
     if (isXml && jsonGenerator instanceof ToXmlGenerator) {
       ToXmlGenerator xmlGenerator = (ToXmlGenerator) jsonGenerator;
       createMetadataInfo(output, xmlGenerator, serializerProvider);
-    } else {
+    }
+    else {
       createMetadataInfo(output, jsonGenerator, serializerProvider);
     }
   }
 
-  private void createMetadataInfo(final Metadata metadata, final JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
+  private void createMetadataInfo(
+      final Metadata metadata,
+      final JsonGenerator jsonGenerator,
+      SerializerProvider serializerProvider)
       throws IOException
   {
     jsonGenerator.writeStartObject();
@@ -53,7 +58,7 @@ public class MetadataSerializer
       new CustomDateSerializer().serialize(metadata.getTimestamp(), jsonGenerator, serializerProvider);
     }
 
-    if(metadata.getLifecycles() != null && shouldSerializeField(metadata, "lifecycles")) {
+    if (metadata.getLifecycles() != null && shouldSerializeField(metadata, "lifecycles")) {
       jsonGenerator.writeFieldName("lifecycles");
       new LifecycleSerializer(isXml).serialize(metadata.getLifecycles(), jsonGenerator, serializerProvider);
     }
@@ -71,23 +76,23 @@ public class MetadataSerializer
       }
     }
 
-    if(metadata.getComponent() != null && shouldSerializeField(metadata, "component")) {
+    if (metadata.getComponent() != null && shouldSerializeField(metadata, "component")) {
       jsonGenerator.writeObjectField("component", metadata.getComponent());
     }
 
-    if(metadata.getManufacture() != null && shouldSerializeField(metadata, "manufacture")) {
-      jsonGenerator.writeObjectField("manufacture", metadata.getManufacture());
+    if (metadata.getManufacturer() != null && shouldSerializeField(metadata, "manufacturer")) {
+      jsonGenerator.writeObjectField("manufacturer", metadata.getManufacturer());
     }
 
-    if(metadata.getManufacturer() != null && shouldSerializeField(metadata, "manufacturer")) {
-      jsonGenerator.writeObjectField("manufacturer", metadata.getManufacturer());
+    if (metadata.getManufacture() != null && shouldSerializeField(metadata, "manufacture")) {
+      jsonGenerator.writeObjectField("manufacture", metadata.getManufacture());
     }
 
-    if(metadata.getSupplier() != null && shouldSerializeField(metadata, "supplier")) {
+    if (metadata.getSupplier() != null && shouldSerializeField(metadata, "supplier")) {
       jsonGenerator.writeObjectField("supplier", metadata.getSupplier());
     }
 
-    if(metadata.getLicenses() != null && shouldSerializeField(metadata, "licenses")) {
+    if (metadata.getLicenses() != null && shouldSerializeField(metadata, "licenses")) {
       jsonGenerator.writeFieldName("licenses");
       new LicenseChoiceSerializer(isXml, version).serialize(metadata.getLicenses(), jsonGenerator, serializerProvider);
     }
@@ -119,7 +124,8 @@ public class MetadataSerializer
       else {
         writeArrayFieldJSON(jsonGenerator, "tools", metadata.getTools());
       }
-    } else if (version.getVersion() >= Version.VERSION_15.getVersion()) {
+    }
+    else if (version.getVersion() >= Version.VERSION_15.getVersion()) {
       ToolInformation choice = metadata.getToolChoice();
       if (choice != null) {
         jsonGenerator.writeFieldName("tools");
@@ -145,7 +151,9 @@ public class MetadataSerializer
     }
   }
 
-  private <T> void writeArrayFieldJSON(JsonGenerator jsonGenerator, String fieldName, List<T> items) throws IOException {
+  private <T> void writeArrayFieldJSON(JsonGenerator jsonGenerator, String fieldName, List<T> items)
+      throws IOException
+  {
     if (items != null) {
       jsonGenerator.writeArrayFieldStart(fieldName);
       for (T item : items) {
@@ -173,7 +181,8 @@ public class MetadataSerializer
       Field field = obj.getClass().getDeclaredField(fieldName);
       VersionFilter filter = field.getAnnotation(VersionFilter.class);
       return filter == null || filter.value().getVersion() <= version.getVersion();
-    } catch (NoSuchFieldException e) {
+    }
+    catch (NoSuchFieldException e) {
       // If the field does not exist, assume it should be serialized
       return true;
     }


Test patch:
diff --git a/src/test/java/org/cyclonedx/parsers/AbstractParserTest.java b/src/test/java/org/cyclonedx/parsers/AbstractParserTest.java
index b74615bb1..7cd651877 100644
--- a/src/test/java/org/cyclonedx/parsers/AbstractParserTest.java
+++ b/src/test/java/org/cyclonedx/parsers/AbstractParserTest.java
@@ -135,8 +135,8 @@ public class AbstractParserTest
     assertNotNull(component.getSwid());
     assertEquals("Acme Application", component.getSwid().getName());
     assertEquals("9.1.1", component.getSwid().getVersion());
-    assertEquals(0, component.getSwid().getTagVersion());
-    assertFalse(component.getSwid().isPatch());
+    assertNull(component.getSwid().getTagVersion());
+    assertNull(component.getSwid().isPatch());
   }
 
   void assertManufacturerMetadata(OrganizationalEntity manufacturer, Version version, boolean deprecated)
@@ -846,8 +846,8 @@ public class AbstractParserTest
     assertEquals("swidgen-242eb18a-503e-ca37-393b-cf156ef09691_9.1.1", component.getSwid().getTagId());
     assertEquals("Acme Application", component.getSwid().getName());
     assertEquals("9.1.1", component.getSwid().getVersion());
-    assertEquals(0, component.getSwid().getTagVersion());
-    assertFalse(component.getSwid().isPatch());
+    assertNull(component.getSwid().getTagVersion());
+    assertNull(component.getSwid().isPatch());
 
     //Release Notes
     ReleaseNotes releaseNotes = metadata.getComponent().getReleaseNotes();
diff --git a/src/test/resources/regression/issue214-expected-output.json b/src/test/resources/regression/issue214-expected-output.json
index f8a7008cb..98121eb0f 100644
--- a/src/test/resources/regression/issue214-expected-output.json
+++ b/src/test/resources/regression/issue214-expected-output.json
@@ -4,6 +4,7 @@
   "version" : 1,
   "components" : [
     {
+      "type" : "library",
       "group" : "org.example",
       "name" : "mylibrary",
       "version" : "1.0.0",
@@ -27,8 +28,7 @@
             }
           ]
         }
-      ],
-      "type" : "library"
+      ]
     }
   ]
 }
\ No newline at end of file