Repository: spdx/tools-python

Issue:
Document node in RDF output has no unique ID
The RDF writer produces a `SpdxDocument` instance that always has the identifier `http://www.spdx.org/tools#SPDXRef-DOCUMENT`. When merging multiple SPDX documents from across multiple distributions, this means that e.g. all package dependencies, the names, the version, the license, creationInfo etc. will be attached to a single node in the RDF graph.

https://github.com/spdx/tools-python/blob/73f3f462591894cac8ef11e01ebec7e702e33c25/spdx/writers/rdf.py#L987

It makes much more sense to use the `name` of the document to construct an identifier as in this line:

https://github.com/spdx/tools-python/blob/73f3f462591894cac8ef11e01ebec7e702e33c25/spdx/writers/rdf.py#L996



Discussion:

Patch:
diff --git a/spdx/writers/rdf.py b/spdx/writers/rdf.py
index c64b36b..ec8b263 100644
--- a/spdx/writers/rdf.py
+++ b/spdx/writers/rdf.py
@@ -248,9 +248,7 @@ class FileWriter(LicenseWriter):
         """
         Create a node for spdx.file.
         """
-        file_node = URIRef(
-            "http://www.spdx.org/files#{id}".format(id=str(doc_file.spdx_id))
-        )
+        file_node = URIRef(f"{self.document.namespace}#{doc_file.spdx_id}")
         type_triple = (file_node, RDF.type, self.spdx_namespace.File)
         self.graph.add(type_triple)
 
@@ -386,7 +384,7 @@ class SnippetWriter(LicenseWriter):
         """
         Return a snippet node.
         """
-        snippet_node = URIRef("http://spdx.org/rdf/terms/Snippet#" + snippet.spdx_id)
+        snippet_node = URIRef(f"{self.document.namespace}#{snippet.spdx_id}")
         type_triple = (snippet_node, RDF.type, self.spdx_namespace.Snippet)
         self.graph.add(type_triple)
 
@@ -793,7 +791,7 @@ class PackageWriter(LicenseWriter):
         Return a Node representing the package.
         Files must have been added to the graph before this method is called.
         """
-        package_node = URIRef(f"http://www.spdx.org/tools#{package.spdx_id}")
+        package_node = URIRef(f"{self.document.namespace}#{package.spdx_id}")
         type_triple = (package_node, RDF.type, self.spdx_namespace.Package)
         self.graph.add(type_triple)
         # Handle optional fields:
@@ -984,7 +982,7 @@ class Writer(
         """
         Add and return the root document node to graph.
         """
-        doc_node = URIRef("http://www.spdx.org/tools#SPDXRef-DOCUMENT")
+        doc_node = URIRef(f"{self.document.namespace}#SPDXRef-DOCUMENT")
         # Doc type
         self.graph.add((doc_node, RDF.type, self.spdx_namespace.SpdxDocument))
         # Version


Test patch:
diff --git a/tests/data/doc_write/rdf-mini.json b/tests/data/doc_write/rdf-mini.json
index d413c6a..282ee17 100644
--- a/tests/data/doc_write/rdf-mini.json
+++ b/tests/data/doc_write/rdf-mini.json
@@ -3,7 +3,7 @@
     "@xmlns:ns1": "http://spdx.org/rdf/terms#",
     "@xmlns:rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
     "ns1:SpdxDocument": {
-      "@rdf:about": "http://www.spdx.org/tools#SPDXRef-DOCUMENT",
+      "@rdf:about": "None#SPDXRef-DOCUMENT",
       "ns1:specVersion": "SPDX-2.1",
       "ns1:dataLicense": {
         "@rdf:resource": "http://spdx.org/licenses/CC0-1.0"
diff --git a/tests/data/doc_write/rdf-simple-plus.json b/tests/data/doc_write/rdf-simple-plus.json
index 9a50011..cc94dce 100644
--- a/tests/data/doc_write/rdf-simple-plus.json
+++ b/tests/data/doc_write/rdf-simple-plus.json
@@ -5,9 +5,9 @@
     "ns1:SpdxDocument": {
       "ns1:describesPackage": {
         "ns1:Package": {
-          "@rdf:about": "http://www.spdx.org/tools#SPDXRef-Package",
+          "@rdf:about": "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Package",
           "ns1:hasFile": {
-            "@rdf:resource": "http://www.spdx.org/files#SPDXRef-File"
+            "@rdf:resource": "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File"
           },
           "ns1:name": "some/path",
           "ns1:licenseDeclared": {
@@ -48,7 +48,7 @@
       },
       "ns1:referencesFile": {
         "ns1:File": {
-          "@rdf:about": "http://www.spdx.org/files#SPDXRef-File",
+          "@rdf:about": "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File",
           "ns1:fileName": "./some/path/tofile", 
           "ns1:checksum": [
             {
@@ -96,7 +96,7 @@
                     }
                 }
             ],
-            "@rdf:about": "http://www.spdx.org/tools#SPDXRef-DOCUMENT"
+            "@rdf:about": "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DOCUMENT"
         }
     }
 }
diff --git a/tests/data/doc_write/rdf-simple.json b/tests/data/doc_write/rdf-simple.json
index 6b1ac2d..e9fb374 100644
--- a/tests/data/doc_write/rdf-simple.json
+++ b/tests/data/doc_write/rdf-simple.json
@@ -5,9 +5,9 @@
     "ns1:SpdxDocument": {
       "ns1:describesPackage": {
         "ns1:Package": {
-          "@rdf:about": "http://www.spdx.org/tools#SPDXRef-Package",
+          "@rdf:about": "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Package",
           "ns1:hasFile": {
-            "@rdf:resource": "http://www.spdx.org/files#SPDXRef-File"
+            "@rdf:resource": "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File"
           },
           "ns1:downloadLocation": {
             "@rdf:resource": "http://spdx.org/rdf/terms#noassertion"
@@ -48,7 +48,7 @@
       },
       "ns1:referencesFile": {
         "ns1:File": {
-          "@rdf:about": "http://www.spdx.org/files#SPDXRef-File",
+          "@rdf:about": "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File",
           "ns1:licenseInfoInFile": {
             "@rdf:resource": "http://spdx.org/licenses/LGPL-2.1-only"
           },
@@ -96,7 +96,7 @@
                     }
                 }
             ],
-      "@rdf:about": "http://www.spdx.org/tools#SPDXRef-DOCUMENT"
+      "@rdf:about": "https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DOCUMENT"
     }
   }
 }