# SBOM format crosswalk — CycloneDX 1.6 vs SPDX 2.3

> SAMPLE — fictional supply-chain data. Every package, registry, version, hash, licence, advisory identifier and signature in this document is invented.

Both serialisations in this category describe the same fictional tree: `orchard-gateway` 4.2.0 and nine invented dependencies. This table is the field map a converter has to implement, and the checklist to diff a conversion against.

| Concept | CycloneDX 1.6 | SPDX 2.3 | Value in these fixtures |
| --- | --- | --- | --- |
| Document id | `serialNumber` (urn:uuid) | `documentNamespace` + `SPDXID` | distinct per file, derived from a fixed seed |
| Root subject | `metadata.component` | `documentDescribes` / DESCRIBES | `@orchard-example/gateway` 4.2.0 |
| Component id | `bom-ref` (purl) | `SPDXID` (`SPDXRef-Package-*`) | one per package |
| Package URL | `purl` | `externalRefs` PACKAGE-MANAGER/purl | identical strings |
| Licence | `licenses[].license.id` or `.expression` | `licenseConcluded` / `licenseDeclared` | same SPDX ids |
| Integrity | `hashes[].alg` + `.content` | `checksums[].algorithm` + `.checksumValue` | same SHA-256 hex |
| Graph edge | `dependencies[].dependsOn` | `Relationship … DEPENDS_ON …` | 10 edges |
| Supplier | `supplier.name` | `PackageSupplier: Organization:` | Example Softworks (fictional) |
| Vulnerabilities | `vulnerabilities[]` (VEX in BOM) | separate VEX/CSAF document | 4 SAMPLE advisories |

## What a correct conversion preserves

1. Component count: **9** dependencies plus the root application.
2. Edge count: **10** dependency relationships.
3. The two diamonds — `example-logger@3.4.1` reached by three parents, `example-json-path@2.0.5` by two — stay single nodes.
4. Every SHA-256 value is byte-identical across formats.
5. Compound licence expressions are not flattened to a single identifier.

## What it legitimately loses

- CycloneDX `services` and `compositions` have no direct SPDX 2.3 equivalent.
- SPDX `snippets` have no CycloneDX equivalent.
- SPDX `LicenseRef-*` extracted text must be carried as a CycloneDX `licenses[].expression` plus a property, or it is dropped.
