Skip to content

Nested cyclonedx.model.dependency.Dependency not serialized to JSON #941

Closed

Description

@stefan6419846

The Dependency class currently allows arbitrary nesting, at least according to the type hints:

def __init__(self, ref: BomRef, dependencies: Optional[Iterable['Dependency']] = None) -> None:
self.ref = ref
self.dependencies = dependencies or []
Serializing such a BOM to JSON will ignore everything except the top-level entry.

Example usage:

bom.dependencies.add(
    Dependency(
        root_component.bom_ref,
        dependencies=[
            Dependency(
                dependency1.bom_ref,
                dependencies=[
                    Dependency(dependency2.bom_ref)
                ]
            )
        ]
    )
)

I stumbled upon this when trying to process external BOMs for further analysis with only looking at the type hints, while I have not been able to build a BOM myself which would actually trigger this after serialization.

Activity

  1. jkowalleck commented on Mar 4, 2026

    @jkowalleck
    Member

    Serializing such a BOM to JSON will ignore everything except the top-level entry.

    have you tried it? could you pull-request a test setup that has any issues?

    tests go here: https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/tests/_data/models.py

  2. stefan6419846 commented on Mar 4, 2026

    @stefan6419846
    ContributorAuthor

    I will have a look at possibly preparing a PR for this tomorrow. For now, this is some reproducing code based upon complex_serialize.py:

    from cyclonedx.model.bom import Bom
    from cyclonedx.model.component import Component, ComponentType
    from cyclonedx.model.dependency import Dependency
    from cyclonedx.output import make_outputter
    from cyclonedx.schema import OutputFormat, SchemaVersion
    
    
    bom = Bom()
    
    bom.metadata.component = root_component = Component(
        name='myApp',
        type=ComponentType.APPLICATION,
        bom_ref='myApp',
    )
    
    component1 = Component(
        type=ComponentType.LIBRARY,
        name='some-component',
        group='acme',
        version='1.33.7-beta.1',
        bom_ref='myComponent@1.33.7-beta.1',
    )
    bom.components.add(component1)
    
    component2 = Component(
        type=ComponentType.LIBRARY,
        name='some-library',
        bom_ref='some-library'
    )
    bom.components.add(component2)
    
    bom.dependencies.add(
        Dependency(
            root_component.bom_ref,
            dependencies=[
                Dependency(
                    component1.bom_ref,
                    dependencies=[
                        Dependency(component2.bom_ref)
                    ]
                )
            ]
        )
    )
    
    my_json_outputter = make_outputter(bom, OutputFormat.JSON, SchemaVersion.V1_7)
    serialized_json = my_json_outputter.output_as_string(indent=2)
    print(serialized_json)
    
    print('', '=' * 30, '', sep='\n')
    
    my_xml_outputter = make_outputter(bom, OutputFormat.XML, SchemaVersion.V1_7)
    serialized_xml = my_xml_outputter.output_as_string(indent=2)
    print(serialized_xml)

    JSON output is this, with nothing depending on some-library:

    {
      "components": [
        {
          "bom-ref": "myComponent@1.33.7-beta.1",
          "group": "acme",
          "name": "some-component",
          "type": "library",
          "version": "1.33.7-beta.1"
        },
        {
          "bom-ref": "some-library",
          "name": "some-library",
          "type": "library"
        }
      ],
      "dependencies": [
        {
          "dependsOn": [
            "myComponent@1.33.7-beta.1"
          ],
          "ref": "myApp"
        },
        {
          "ref": "myComponent@1.33.7-beta.1"
        },
        {
          "ref": "some-library"
        }
      ],
      "metadata": {
        "component": {
          "bom-ref": "myApp",
          "name": "myApp",
          "type": "application"
        },
        "timestamp": "2026-03-04T17:33:26.085743+00:00"
      },
      "serialNumber": "urn:uuid:9c7da534-f964-4d74-a09e-9c5770262989",
      "version": 1,
      "$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
      "bomFormat": "CycloneDX",
      "specVersion": "1.7"
    }

    The XML looks correct (myComponent@1.33.7-beta.1 referencing some-library):

    <?xml version="1.0" ?>
    <bom xmlns="http://cyclonedx.org/schema/bom/1.7" serialNumber="urn:uuid:9c7da534-f964-4d74-a09e-9c5770262989" version="1">
      <metadata>
        <timestamp>2026-03-04T17:33:26.085743+00:00</timestamp>
        <component type="application" bom-ref="myApp">
          <name>myApp</name>
        </component>
      </metadata>
      <components>
        <component type="library" bom-ref="myComponent@1.33.7-beta.1">
          <group>acme</group>
          <name>some-component</name>
          <version>1.33.7-beta.1</version>
        </component>
        <component type="library" bom-ref="some-library">
          <name>some-library</name>
        </component>
      </components>
      <dependencies>
        <dependency ref="myApp">
          <dependency ref="myComponent@1.33.7-beta.1">
            <dependency ref="some-library"/>
          </dependency>
        </dependency>
        <dependency ref="myComponent@1.33.7-beta.1"/>
        <dependency ref="some-library"/>
      </dependencies>
    </bom>
  3. changed the title [-]cyclonedx.model.dependency.Dependency claims to be nestable, although not serialized[/-] [+]Nested cyclonedx.model.dependency.Dependency not serialized to JSON[/+] on Mar 4, 2026
  4. jkowalleck commented on Jun 8, 2026

    @jkowalleck
    Member

    looking at #942
    i can not see any issues.

    The test data looks about right:

    • you modeled nested dependencies
    • CycloneDX XML uses allowed nested dependencies - and renders them as expected
    • CycloneDX JSON does not allow nested dependencies - and omits them as expected

    this means:
    you are correctly able to model the dependency graph according to CycloneDX XML and JSON implementation/schema.

    @stefan6419846 , could you elaborate on the topic? what is the question/issue here? what is your exact use case?

  5. stefan6419846 commented on Jun 8, 2026

    @stefan6419846
    ContributorAuthor

    could you elaborate on the topic? what is the question/issue here? what is your exact use case?

    I have a BOM importer and wrote some tests where I used the XML-only Python-structure for JSON by mostly looking at the attributes exposed by the different classes. This worked without any warning and can lead to data loss. I would (at least) have expected to receive a warning or error on serialization about this being not supported for JSON.

  6. jkowalleck commented on Jun 8, 2026

    @jkowalleck
    Member

    could you elaborate on the topic? what is the question/issue here? what is your exact use case?

    I have a BOM importer and wrote some tests where I used the XML-only Python-structure for JSON by mostly looking at the attributes exposed by the different classes. This worked without any warning and can lead to data loss. I would (at least) have expected to receive a warning or error on serialization about this being not supported for JSON.

    Ah, i see. the topic is not modeling, but conversion.

    Possible solutions I see:
    have a 'contrib' module that flattens a dependency graph's data models
    or have this flattening as part of JSON serialization transparently.

    did you have a solution in mind already?

  7. stefan6419846 commented on Jun 8, 2026

    @stefan6419846
    ContributorAuthor

    I did not yet have any further thoughts about the best solution for this case, as my testing (luckily) showed this rather quickly, although it took me some time to further debug this.

    The problem I see with a contrib module possibly being required to be called for flattening wouldn't be obvious for me. Thus I would rather see this being flattened automatically or detected automatically with a hint to the contrib function, preventing unexpected data loss in the first place due to "conflicting" serializiation options.

  8. jkowalleck commented on Jun 8, 2026

    @jkowalleck
    Member

    understood.

    will work on a fix .

  9. self-assigned this
    on Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions