Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.cyclonedx.Format;
import org.cyclonedx.Version;
import org.cyclonedx.model.Bom;
import org.cyclonedx.util.serializer.AuthorsBeanSerializerModifier;
import org.cyclonedx.util.serializer.CustomSerializerModifier;
import org.cyclonedx.util.serializer.EvidenceSerializer;
import org.cyclonedx.util.serializer.ExternalReferenceSerializer;
Expand Down Expand Up @@ -93,6 +94,10 @@ protected void setupObjectMapper(boolean isXml) {
hash1Module.addSerializer(new HashSerializer(version));
mapper.registerModule(hash1Module);

SimpleModule authorsModule = new SimpleModule();
authorsModule.setSerializerModifier(new AuthorsBeanSerializerModifier(version));
mapper.registerModule(authorsModule);

SimpleModule propertiesModule = new SimpleModule();
propertiesModule.setSerializerModifier(new CustomSerializerModifier(isXml, version));
mapper.registerModule(propertiesModule);
Expand Down
33 changes: 32 additions & 1 deletion src/main/java/org/cyclonedx/model/Component.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
import java.util.List;
import java.util.Objects;

import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import org.cyclonedx.Version;
import org.cyclonedx.model.component.ModelCard;
Expand Down Expand Up @@ -223,7 +225,6 @@ public String getScopeName() {
private Tags tags;

@VersionFilter(Version.VERSION_16)
@JsonProperty("authors")
private List<OrganizationalContact> authors;

@VersionFilter(Version.VERSION_16)
Expand Down Expand Up @@ -258,10 +259,26 @@ public void setSupplier(OrganizationalEntity supplier) {
this.supplier = supplier;
}

/**
* Gets the deprecated author field as a string.
* @return the author name as a string
* @deprecated since version 1.6, use {@link #getAuthors()} instead
*/
@Deprecated
@JsonGetter("author")
@JacksonXmlProperty(localName = "author")
public String getAuthor() {
return author;
}

/**
* Sets the deprecated author field as a string.
* @param author the author name as a string
* @deprecated since version 1.6, use {@link #setAuthors(List)} instead
*/
@Deprecated
@JsonSetter("author")
@JacksonXmlProperty(localName = "author")
public void setAuthor(String author) {
this.author = author;
}
Expand Down Expand Up @@ -556,10 +573,24 @@ public void setTags(final Tags tags) {
this.tags = tags;
}

/**
* Gets the component authors as a list of contacts.
* This replaces the deprecated string-based author field.
* @return the list of authors, or null if not set
* @since 1.6
*/
@JsonGetter("authors")
public List<OrganizationalContact> getAuthors() {
return authors;
}

/**
* Sets the component authors as a list of contacts.
* This replaces the deprecated string-based author field.
* @param authors the list of authors
* @since 1.6
*/
@JsonSetter("authors")
public void setAuthors(final List<OrganizationalContact> authors) {
this.authors = authors;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
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.JacksonXmlRootElement;
import org.cyclonedx.Version;

import java.util.Objects;

@JacksonXmlRootElement(localName = "author")
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(Include.NON_EMPTY)
@JsonPropertyOrder({"name", "email", "phone"})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public boolean hasIgnoreMarker(final AnnotatedMember m) {

// Check if the field has the XmlOnly annotation
if (m.hasAnnotation(XmlOnly.class)) {
// If true, the field should be ignored for XML serialization
// If true, the field should be ignored for JSON serialization
return true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* 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 com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import org.cyclonedx.Version;
import org.cyclonedx.model.Component;

import java.util.List;

/**
* Bean serializer modifier for Component.authors field.
* Applies the AuthorsSerializer only to the authors field in Component class.
*/
public class AuthorsBeanSerializerModifier extends BeanSerializerModifier {
private final Version version;

public AuthorsBeanSerializerModifier(Version version) {
this.version = version;
}

@Override
public List<BeanPropertyWriter> changeProperties(
SerializationConfig config,
BeanDescription beanDesc,
List<BeanPropertyWriter> beanProperties) {

// Only modify Component class
if (Component.class.isAssignableFrom(beanDesc.getBeanClass())) {
java.util.Iterator<BeanPropertyWriter> iterator = beanProperties.iterator();
while (iterator.hasNext()) {
BeanPropertyWriter writer = iterator.next();
// Find the authors property
if ("authors".equals(writer.getName())) {
// Check if the current version supports the authors field (v1.6+)
if (version.getVersion() < Version.VERSION_16.getVersion()) {
// Remove the property for versions earlier than 1.6
iterator.remove();
} else {
// Assign the custom serializer for v1.6+
JsonSerializer<?> serializer = new AuthorsSerializer();
writer.assignSerializer((JsonSerializer<Object>) serializer);
}
break;
}
}
}
return beanProperties;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* 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 com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import org.cyclonedx.model.OrganizationalContact;

import java.io.IOException;
import java.util.List;

/**
* Custom serializer for the Component.authors field to handle XML element naming.
* This serializer ensures that:
* - JSON: serializes as "authors": [...]
* - XML: serializes as authors-author
* This is necessary because the deprecated "author" field also uses author element,
* creating a naming conflict that standard Jackson annotations cannot resolve.
* Version filtering is handled by AuthorsBeanSerializerModifier, which removes this
* property entirely for versions prior to 1.6.
*/
public class AuthorsSerializer extends StdSerializer<List<OrganizationalContact>> {

@SuppressWarnings("unchecked")
public AuthorsSerializer() {
super((Class<List<OrganizationalContact>>) (Class<?>) List.class);
}

@Override
public void serialize(List<OrganizationalContact> authors, JsonGenerator gen, SerializerProvider serializers)
throws IOException {

if (authors == null || authors.isEmpty()) {
return;
}

// Check if we're serializing to XML
if (gen instanceof ToXmlGenerator) {
ToXmlGenerator xmlGen = (ToXmlGenerator) gen;

// For XML: The property name "authors" creates the wrapper <authors>
// We need to write the array structure, and each item gets its own <author> tag
xmlGen.writeStartArray();
for (OrganizationalContact contact : authors) {
// Set the element name for this array item to "author"
xmlGen.setNextName(new javax.xml.namespace.QName("author"));
serializers.defaultSerializeValue(contact, xmlGen);
}
xmlGen.writeEndArray();
} else {
// For JSON, write as a standard array
gen.writeStartArray();
for (OrganizationalContact contact : authors) {
serializers.defaultSerializeValue(contact, gen);
}
gen.writeEndArray();
}
}
}
Loading
Loading