Repository: spdx/tools-python

Issue:
Ignore duplicated relationships while parsing
While parsing [this file](https://github.com/spdx/spdx-spec/blob/efa69656db9db4fcc871de563cbbd104fc1e33c3/examples/SPDXJSONExample-v2.3.spdx.json#L111) I noticed that the duplicated `hasFiles` entries lead to duplicated relationships in the data model. We might want to avoid that.

Discussion:

Patch:
diff --git a/src/spdx/jsonschema/file_converter.py b/src/spdx/jsonschema/file_converter.py
index 78af0b9..5806ee6 100644
--- a/src/spdx/jsonschema/file_converter.py
+++ b/src/spdx/jsonschema/file_converter.py
@@ -62,7 +62,7 @@ class FileConverter(TypedConverter[File]):
         elif file_property == FileProperty.LICENSE_COMMENTS:
             return file.license_comment
         elif file_property == FileProperty.LICENSE_CONCLUDED:
-            return apply_if_present(str, file.concluded_license)
+            return apply_if_present(str, file.license_concluded)
         elif file_property == FileProperty.LICENSE_INFO_IN_FILES:
             if isinstance(file.license_info_in_file, list):
                 return [str(license_expression) for license_expression in file.license_info_in_file] or None
diff --git a/src/spdx/jsonschema/snippet_converter.py b/src/spdx/jsonschema/snippet_converter.py
index 0d3013e..90dc988 100644
--- a/src/spdx/jsonschema/snippet_converter.py
+++ b/src/spdx/jsonschema/snippet_converter.py
@@ -46,7 +46,7 @@ class SnippetConverter(TypedConverter[Snippet]):
         elif snippet_property == SnippetProperty.LICENSE_COMMENTS:
             return snippet.license_comment
         elif snippet_property == SnippetProperty.LICENSE_CONCLUDED:
-            return apply_if_present(str, snippet.concluded_license)
+            return apply_if_present(str, snippet.license_concluded)
         elif snippet_property == SnippetProperty.LICENSE_INFO_IN_SNIPPETS:
             if isinstance(snippet.license_info_in_snippet, list):
                 return [str(license_expression) for license_expression in snippet.license_info_in_snippet] or None
diff --git a/src/spdx/model/file.py b/src/spdx/model/file.py
index fa23f54..7783775 100644
--- a/src/spdx/model/file.py
+++ b/src/spdx/model/file.py
@@ -40,7 +40,7 @@ class File:
     spdx_id: str
     checksums: List[Checksum]
     file_type: List[FileType] = field(default_factory=list)
-    concluded_license: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None
+    license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None
     license_info_in_file: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = field(
         default_factory=list)
     license_comment: Optional[str] = None
@@ -56,7 +56,7 @@ class File:
     #   between the file and this package
 
     def __init__(self, name: str, spdx_id: str, checksums: List[Checksum], file_type: List[FileType] = None,
-                 concluded_license: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None,
+                 license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None,
                  license_info_in_file: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = None,
                  license_comment: Optional[str] = None,
                  copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None,
diff --git a/src/spdx/model/snippet.py b/src/spdx/model/snippet.py
index 5fba0c6..1d5fa6b 100644
--- a/src/spdx/model/snippet.py
+++ b/src/spdx/model/snippet.py
@@ -24,7 +24,7 @@ class Snippet:
     file_spdx_id: str
     byte_range: Tuple[int, int]
     line_range: Optional[Tuple[int, int]] = None
-    concluded_license: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None
+    license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None
     license_info_in_snippet: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = None
     license_comment: Optional[str] = None
     copyright_text: Optional[Union[str, SpdxNoAssertion, SpdxNone]] = None
@@ -34,7 +34,7 @@ class Snippet:
 
     def __init__(self, spdx_id: str, file_spdx_id: str, byte_range: Tuple[int, int],
                  line_range: Optional[Tuple[int, int]] = None,
-                 concluded_license: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None,
+                 license_concluded: Optional[Union[LicenseExpression, SpdxNoAssertion, SpdxNone]] = None,
                  license_info_in_snippet: Optional[Union[List[LicenseExpression], SpdxNoAssertion, SpdxNone]] = None,
                  license_comment: Optional[str] = None, copyright_text: Optional[str] = None,
                  comment: Optional[str] = None, name: Optional[str] = None, attribution_texts: List[str] = None):
diff --git a/src/spdx/parser/jsonlikedict/dict_parsing_functions.py b/src/spdx/parser/jsonlikedict/dict_parsing_functions.py
index f5d3610..c2cde1e 100644
--- a/src/spdx/parser/jsonlikedict/dict_parsing_functions.py
+++ b/src/spdx/parser/jsonlikedict/dict_parsing_functions.py
@@ -92,3 +92,8 @@ def parse_list_of_elements(list_of_elements: List[Dict], method_to_parse_element
                                                            method_to_parse_element)
     raise_parsing_error_if_logger_has_messages(logger)
     return parsed_elements
+
+
+def delete_duplicates_from_list(list_with_potential_duplicates: List[Any]) -> List[Any]:
+    list_without_duplicates = list(dict.fromkeys(list_with_potential_duplicates))
+    return list_without_duplicates
diff --git a/src/spdx/parser/jsonlikedict/file_parser.py b/src/spdx/parser/jsonlikedict/file_parser.py
index 44a62b5..57954c0 100644
--- a/src/spdx/parser/jsonlikedict/file_parser.py
+++ b/src/spdx/parser/jsonlikedict/file_parser.py
@@ -64,7 +64,7 @@ class FileParser:
                                                            copyright_text=copyright_text, file_type=file_types,
                                                            contributors=file_contributors,
                                                            license_comment=license_comments,
-                                                           concluded_license=license_concluded,
+                                                           license_concluded=license_concluded,
                                                            license_info_in_file=license_info_in_files,
                                                            notice=notice_text)
                                                 )
diff --git a/src/spdx/parser/jsonlikedict/relationship_parser.py b/src/spdx/parser/jsonlikedict/relationship_parser.py
index f17a637..2804eb6 100644
--- a/src/spdx/parser/jsonlikedict/relationship_parser.py
+++ b/src/spdx/parser/jsonlikedict/relationship_parser.py
@@ -13,9 +13,10 @@ from typing import Dict, List, Optional
 from spdx.model.relationship import Relationship, RelationshipType
 from spdx.model.typing.constructor_type_errors import ConstructorTypeErrors
 from spdx.parser.error import SPDXParsingError
-from spdx.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, json_str_to_enum_name, \
+from spdx.parser.jsonlikedict.dict_parsing_functions import raise_parsing_error_if_logger_has_messages, \
+    json_str_to_enum_name, \
     construct_or_raise_parsing_error, \
-    parse_field_or_log_error, parse_field_or_no_assertion_or_none
+    parse_field_or_log_error, parse_field_or_no_assertion_or_none, delete_duplicates_from_list
 from spdx.parser.logger import Logger
 
 
@@ -31,7 +32,7 @@ class RelationshipParser:
         relationships.extend(
             parse_field_or_log_error(self.logger, relationship_dicts, self.parse_relationship, [], True))
 
-        document_describes: List[str] = input_doc_dict.get("documentDescribes", [])
+        document_describes: List[str] = delete_duplicates_from_list(input_doc_dict.get("documentDescribes", []))
         doc_spdx_id: Optional[str] = input_doc_dict.get("SPDXID")
 
         relationships.extend(
@@ -102,7 +103,7 @@ class RelationshipParser:
         contains_relationships = []
         for package in package_dicts:
             package_spdx_id: Optional[str] = package.get("SPDXID")
-            contained_files: Optional[str] = package.get("hasFiles")
+            contained_files: List[str] = delete_duplicates_from_list(package.get("hasFiles", []))
             if not contained_files:
                 continue
             for file_spdx_id in contained_files:
diff --git a/src/spdx/parser/jsonlikedict/snippet_parser.py b/src/spdx/parser/jsonlikedict/snippet_parser.py
index f0a8fef..9dbb733 100644
--- a/src/spdx/parser/jsonlikedict/snippet_parser.py
+++ b/src/spdx/parser/jsonlikedict/snippet_parser.py
@@ -52,7 +52,7 @@ class SnippetParser:
         comment: Optional[str] = snippet_dict.get("comment")
         copyright_text: Optional[str] = snippet_dict.get("copyrightText")
         license_comment: Optional[str] = snippet_dict.get("licenseComments")
-        concluded_license: Optional[Union[
+        license_concluded: Optional[Union[
             LicenseExpression, SpdxNoAssertion, SpdxNone]] = parse_field_or_log_error(logger, snippet_dict.get(
             "licenseConcluded"), lambda x: parse_field_or_no_assertion_or_none(x,
                                                                                self.license_expression_parser.parse_license_expression))
@@ -69,7 +69,7 @@ class SnippetParser:
                                                         file_spdx_id=file_spdx_id, line_range=line_range,
                                                         attribution_texts=attribution_texts, comment=comment,
                                                         copyright_text=copyright_text, license_comment=license_comment,
-                                                        concluded_license=concluded_license,
+                                                        license_concluded=license_concluded,
                                                         license_info_in_snippet=license_info))
 
         return snippet
diff --git a/src/spdx/validation/file_validator.py b/src/spdx/validation/file_validator.py
index 33cc732..cc4375e 100644
--- a/src/spdx/validation/file_validator.py
+++ b/src/spdx/validation/file_validator.py
@@ -65,7 +65,7 @@ def validate_file(file: File, context: Optional[ValidationContext] = None) -> Li
 
     validation_messages.extend(validate_checksums(file.checksums, file.spdx_id))
 
-    validation_messages.extend(validate_license_expression(file.concluded_license))
+    validation_messages.extend(validate_license_expression(file.license_concluded))
 
     validation_messages.extend(validate_license_expressions(file.license_info_in_file))
 
diff --git a/src/spdx/validation/snippet_validator.py b/src/spdx/validation/snippet_validator.py
index 3140a85..4d350f3 100644
--- a/src/spdx/validation/snippet_validator.py
+++ b/src/spdx/validation/snippet_validator.py
@@ -83,7 +83,7 @@ def validate_snippet(snippet: Snippet, context: Optional[ValidationContext] = No
                     context)
             )
 
-    validation_messages.extend(validate_license_expression(snippet.concluded_license))
+    validation_messages.extend(validate_license_expression(snippet.license_concluded))
 
     validation_messages.extend(validate_license_expressions(snippet.license_info_in_snippet))
 
diff --git a/src/spdx/writer/tagvalue/file_writer.py b/src/spdx/writer/tagvalue/file_writer.py
index e281d4f..ce1ec0a 100644
--- a/src/spdx/writer/tagvalue/file_writer.py
+++ b/src/spdx/writer/tagvalue/file_writer.py
@@ -28,7 +28,7 @@ def write_file(file: File, text_output: TextIO):
     for file_checksum in file.checksums:
         write_value("FileChecksum", write_checksum_to_tag_value(file_checksum), text_output)
 
-    write_license_expression("LicenseConcluded", file.concluded_license, text_output)
+    write_license_expression("LicenseConcluded", file.license_concluded, text_output)
     write_license_expression("LicenseInfoInFile", file.license_info_in_file, text_output)
     write_text_value("LicenseComments", file.license_comment, text_output)
     write_text_value("FileCopyrightText", file.copyright_text, text_output)
diff --git a/src/spdx/writer/tagvalue/snippet_writer.py b/src/spdx/writer/tagvalue/snippet_writer.py
index 7f5d8d6..dc8553e 100644
--- a/src/spdx/writer/tagvalue/snippet_writer.py
+++ b/src/spdx/writer/tagvalue/snippet_writer.py
@@ -23,7 +23,7 @@ def write_snippet(snippet: Snippet, output_text: TextIO):
     write_range("SnippetByteRange", snippet.byte_range, output_text)
     write_range("SnippetLineRange", snippet.line_range, output_text)
 
-    write_license_expression("SnippetLicenseConcluded", snippet.concluded_license, output_text)
+    write_license_expression("SnippetLicenseConcluded", snippet.license_concluded, output_text)
     write_license_expression("LicenseInfoInSnippet", snippet.license_info_in_snippet, output_text)
     write_text_value("SnippetLicenseComments", snippet.license_comment, output_text)
     write_text_value("SnippetCopyrightText", snippet.copyright_text, output_text)


Test patch:
diff --git a/tests/fixtures.py b/tests/fixtures.py
index 8bcb0f3..389fa56 100644
--- a/tests/fixtures.py
+++ b/tests/fixtures.py
@@ -53,7 +53,7 @@ def creation_info_fixture(spdx_version="SPDX-2.3", spdx_id="SPDXRef-DOCUMENT", n
 
 
 def file_fixture(name="./fileName.py", spdx_id="SPDXRef-File", checksums=None, file_type=None,
-                 concluded_license=LicenseExpression("concludedLicenseExpression"), license_info_in_file=None,
+                 license_concluded=LicenseExpression("licenseConcludedExpression"), license_info_in_file=None,
                  license_comment="licenseComment", copyright_text="copyrightText", comment="fileComment",
                  notice="fileNotice", contributors=None, attribution_texts=None) -> File:
     checksums = [checksum_fixture()] if checksums is None else checksums
@@ -63,7 +63,7 @@ def file_fixture(name="./fileName.py", spdx_id="SPDXRef-File", checksums=None, f
     contributors = ["fileContributor"] if contributors is None else contributors
     attribution_texts = ["fileAttributionText"] if attribution_texts is None else attribution_texts
     return File(name=name, spdx_id=spdx_id, checksums=checksums, file_type=file_type,
-                concluded_license=concluded_license, license_info_in_file=license_info_in_file,
+                license_concluded=license_concluded, license_info_in_file=license_info_in_file,
                 license_comment=license_comment, copyright_text=copyright_text, comment=comment, notice=notice,
                 contributors=contributors, attribution_texts=attribution_texts)
 
@@ -108,7 +108,7 @@ def external_package_ref_fixture(category=ExternalPackageRefCategory.PACKAGE_MAN
 
 
 def snippet_fixture(spdx_id="SPDXRef-Snippet", file_spdx_id="SPDXRef-File", byte_range=(1, 2),
-                    line_range=(3, 4), concluded_license=LicenseExpression("snippetLicenseConcluded"),
+                    line_range=(3, 4), license_concluded=LicenseExpression("snippetLicenseConcluded"),
                     license_info_in_snippet=None, license_comment="snippetLicenseComment",
                     copyright_text="licenseCopyrightText", comment="snippetComment", name="snippetName",
                     attribution_texts=None) -> Snippet:
@@ -116,7 +116,7 @@ def snippet_fixture(spdx_id="SPDXRef-Snippet", file_spdx_id="SPDXRef-File", byte
         LicenseExpression("licenseInfoInSnippet")] if license_info_in_snippet is None else license_info_in_snippet
     attribution_texts = ["snippetAttributionText"] if attribution_texts is None else attribution_texts
     return Snippet(spdx_id=spdx_id, file_spdx_id=file_spdx_id, byte_range=byte_range, line_range=line_range,
-                   concluded_license=concluded_license, license_info_in_snippet=license_info_in_snippet,
+                   license_concluded=license_concluded, license_info_in_snippet=license_info_in_snippet,
                    license_comment=license_comment, copyright_text=copyright_text, comment=comment, name=name,
                    attribution_texts=attribution_texts)
 
diff --git a/tests/jsonschema/test_file_converter.py b/tests/jsonschema/test_file_converter.py
index 452d08c..f0e968a 100644
--- a/tests/jsonschema/test_file_converter.py
+++ b/tests/jsonschema/test_file_converter.py
@@ -73,7 +73,7 @@ def test_successful_conversion(converter: FileConverter):
     converter.annotation_converter.convert.return_value = "mock_converted_annotation"
     file = File(name="name", spdx_id="spdxId",
                 checksums=[Checksum(ChecksumAlgorithm.SHA224, "sha224"), Checksum(ChecksumAlgorithm.MD2, "md2")],
-                file_type=[FileType.SPDX, FileType.OTHER], concluded_license=LicenseExpression("licenseExpression1"),
+                file_type=[FileType.SPDX, FileType.OTHER], license_concluded=LicenseExpression("licenseExpression1"),
                 license_info_in_file=[LicenseExpression("licenseExpression2"), LicenseExpression("licenseExpression3")],
                 license_comment="licenseComment", copyright_text="copyrightText", comment="comment", notice="notice",
                 contributors=["contributor1", "contributor2"],
@@ -103,7 +103,7 @@ def test_successful_conversion(converter: FileConverter):
 
 
 def test_null_values(converter: FileConverter):
-    file = file_fixture(copyright_text=None, concluded_license=None, license_comment=None, comment=None, notice=None,
+    file = file_fixture(copyright_text=None, license_concluded=None, license_comment=None, comment=None, notice=None,
                         attribution_texts=[], checksums=[], contributors=[], file_type=[], license_info_in_file=[])
     document = Document(creation_info_fixture(), files=[file])
 
@@ -123,7 +123,7 @@ def test_null_values(converter: FileConverter):
 
 
 def test_spdx_no_assertion(converter: FileConverter):
-    file = file_fixture(concluded_license=SpdxNoAssertion(), license_info_in_file=SpdxNoAssertion(),
+    file = file_fixture(license_concluded=SpdxNoAssertion(), license_info_in_file=SpdxNoAssertion(),
                         copyright_text=SpdxNoAssertion())
     document = Document(creation_info_fixture(), files=[file])
 
@@ -136,7 +136,7 @@ def test_spdx_no_assertion(converter: FileConverter):
 
 
 def test_spdx_none(converter: FileConverter):
-    file = file_fixture(concluded_license=SpdxNone(), license_info_in_file=SpdxNone(), copyright_text=SpdxNone())
+    file = file_fixture(license_concluded=SpdxNone(), license_info_in_file=SpdxNone(), copyright_text=SpdxNone())
     document = Document(creation_info_fixture(), files=[file])
 
     converted_dict = converter.convert(file, document)
diff --git a/tests/jsonschema/test_snippet_converter.py b/tests/jsonschema/test_snippet_converter.py
index c29ad08..788bb43 100644
--- a/tests/jsonschema/test_snippet_converter.py
+++ b/tests/jsonschema/test_snippet_converter.py
@@ -66,7 +66,7 @@ def test_successful_conversion(converter: SnippetConverter):
     converter.annotation_converter.convert.return_value = "mock_converted_annotation"
     file_spdx_id = "fileSpdxId"
     snippet = Snippet("spdxId", file_spdx_id=file_spdx_id, byte_range=(1, 2), line_range=(3, 4),
-                      concluded_license=LicenseExpression("licenseExpression1"),
+                      license_concluded=LicenseExpression("licenseExpression1"),
                       license_info_in_snippet=[LicenseExpression("licenseExpression2"),
                                                LicenseExpression("licenseExpression3")],
                       license_comment="licenseComment", copyright_text="copyrightText", comment="comment", name="name",
@@ -98,7 +98,7 @@ def test_successful_conversion(converter: SnippetConverter):
 
 
 def test_null_values(converter: SnippetConverter):
-    snippet = snippet_fixture(concluded_license=None, license_comment=None, copyright_text=None, comment=None,
+    snippet = snippet_fixture(license_concluded=None, license_comment=None, copyright_text=None, comment=None,
                               name=None, attribution_texts=[], license_info_in_snippet=[])
 
     document = Document(creation_info_fixture(), snippets=[snippet])
@@ -115,7 +115,7 @@ def test_null_values(converter: SnippetConverter):
 
 
 def test_spdx_no_assertion(converter: SnippetConverter):
-    snippet = snippet_fixture(concluded_license=SpdxNoAssertion(), license_info_in_snippet=SpdxNoAssertion())
+    snippet = snippet_fixture(license_concluded=SpdxNoAssertion(), license_info_in_snippet=SpdxNoAssertion())
 
     document = Document(creation_info_fixture(), snippets=[snippet])
     converted_dict = converter.convert(snippet, document)
@@ -126,7 +126,7 @@ def test_spdx_no_assertion(converter: SnippetConverter):
 
 
 def test_spdx_none(converter: SnippetConverter):
-    snippet = snippet_fixture(concluded_license=SpdxNone(), license_info_in_snippet=SpdxNone())
+    snippet = snippet_fixture(license_concluded=SpdxNone(), license_info_in_snippet=SpdxNone())
 
     document = Document(creation_info_fixture(), snippets=[snippet])
     converted_dict = converter.convert(snippet, document)
diff --git a/tests/model/test_file.py b/tests/model/test_file.py
index 40a77d7..14d6b4c 100644
--- a/tests/model/test_file.py
+++ b/tests/model/test_file.py
@@ -16,7 +16,7 @@ def test_correct_initialization(checksum):
     assert file.spdx_id == "id"
     assert file.checksums == [checksum, checksum]
     assert file.file_type == [FileType.OTHER, FileType.SPDX]
-    assert file.concluded_license == SpdxNone()
+    assert file.license_concluded == SpdxNone()
     assert file.license_info_in_file == SpdxNoAssertion()
     assert file.license_comment == "comment on license"
     assert file.copyright_text == "copyright"
@@ -33,7 +33,7 @@ def test_correct_initialization_with_default_values(checksum):
     assert file.spdx_id == "id"
     assert file.checksums == [checksum, checksum]
     assert file.file_type == []
-    assert file.concluded_license is None
+    assert file.license_concluded is None
     assert file.license_info_in_file == []
     assert file.license_comment is None
     assert file.copyright_text is None
@@ -68,9 +68,9 @@ def test_wrong_type_in_file_type(checksum):
 
 
 @mock.patch('spdx.model.checksum.Checksum', autospec=True)
-def test_wrong_type_in_concluded_license(checksum):
+def test_wrong_type_in_license_concluded(checksum):
     with pytest.raises(TypeError):
-        File("name", "id", [checksum], concluded_license="NONE")
+        File("name", "id", [checksum], license_concluded="NONE")
 
 
 @mock.patch('spdx.model.checksum.Checksum', autospec=True)
diff --git a/tests/model/test_snippet.py b/tests/model/test_snippet.py
index 779e0c6..23bc2b6 100644
--- a/tests/model/test_snippet.py
+++ b/tests/model/test_snippet.py
@@ -12,7 +12,7 @@ def test_correct_initialization():
     assert snippet.file_spdx_id == "file_id"
     assert snippet.byte_range == (200, 400)
     assert snippet.line_range == (20, 40)
-    assert snippet.concluded_license == SpdxNone()
+    assert snippet.license_concluded == SpdxNone()
     assert snippet.license_info_in_snippet == SpdxNoAssertion()
     assert snippet.license_comment == "comment on license"
     assert snippet.copyright_text == "copyright"
@@ -27,7 +27,7 @@ def test_correct_initialization_with_default_values():
     assert snippet.file_spdx_id == "file_id"
     assert snippet.byte_range == (200, 400)
     assert snippet.line_range is None
-    assert snippet.concluded_license is None
+    assert snippet.license_concluded is None
     assert snippet.license_info_in_snippet is None
     assert snippet.license_comment is None
     assert snippet.copyright_text is None
@@ -56,9 +56,9 @@ def test_wrong_type_in_line_range():
         Snippet("id", "file_id", (200, 400), line_range=(20, "40"))
 
 
-def test_wrong_type_in_concluded_license():
+def test_wrong_type_in_license_concluded():
     with pytest.raises(TypeError):
-        Snippet("id", "file_id", (200, 400), concluded_license="NONE")
+        Snippet("id", "file_id", (200, 400), license_concluded="NONE")
 
 
 def test_wrong_type_in_license_info_in_snippet():
diff --git a/tests/parser/json/test_json_parser.py b/tests/parser/json/test_json_parser.py
index 3acbfa1..6939db9 100644
--- a/tests/parser/json/test_json_parser.py
+++ b/tests/parser/json/test_json_parser.py
@@ -31,7 +31,7 @@ def test_parse_json_with_2_3_example():
     assert len(doc.files) == 5
     assert len(doc.packages) == 4
     assert len(doc.snippets) ==  1
-    assert len(doc.relationships) == 23
+    assert len(doc.relationships) == 13
     assert len(doc.extracted_licensing_info) == 5
 
 def test_parse_json_with_2_2_example():
diff --git a/tests/parser/jsonlikedict/test_file_parser.py b/tests/parser/jsonlikedict/test_file_parser.py
index ea8eabe..8a06f63 100644
--- a/tests/parser/jsonlikedict/test_file_parser.py
+++ b/tests/parser/jsonlikedict/test_file_parser.py
@@ -56,7 +56,7 @@ def test_parse_file():
     assert file.file_type == [FileType.SOURCE]
     TestCase().assertCountEqual(file.contributors, ["The Regents of the University of California",
                                                     "Modified by Paul Mundt lethal@linux-sh.org", "IBM Corporation"])
-    assert file.concluded_license == LicenseExpression("(LGPL-2.0-only OR LicenseRef-2)")
+    assert file.license_concluded == LicenseExpression("(LGPL-2.0-only OR LicenseRef-2)")
     TestCase().assertCountEqual(file.license_info_in_file,
                                 [LicenseExpression("GPL-2.0-only"), LicenseExpression("LicenseRef-2")])
     assert file.license_comment == "The concluded license was taken from the package level that the file was included in."
diff --git a/tests/parser/jsonlikedict/test_relationship_parser.py b/tests/parser/jsonlikedict/test_relationship_parser.py
index 20dc39a..79dae72 100644
--- a/tests/parser/jsonlikedict/test_relationship_parser.py
+++ b/tests/parser/jsonlikedict/test_relationship_parser.py
@@ -79,26 +79,39 @@ def test_parse_document_describes():
                                  Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-Snippet")])
 
 
-def test_parse_document_describes_without_duplicating_relationships():
+@pytest.mark.parametrize("document_describes,relationships,parsed_relationships",
+                         [(["SPDXRef-Package", "SPDXRef-File"], [
+                             {"spdxElementId": "SPDXRef-DOCUMENT", "relatedSpdxElement": "SPDXRef-Package",
+                              "relationshipType": "DESCRIBES", "comment": "This relationship has a comment."},
+                             {"spdxElementId": "SPDXRef-File", "relatedSpdxElement": "SPDXRef-DOCUMENT",
+                              "relationshipType": "DESCRIBED_BY", "comment": "This relationship has a comment."}], [
+                               Relationship(related_spdx_element_id="SPDXRef-Package",
+                                            relationship_type=RelationshipType.DESCRIBES,
+                                            spdx_element_id="SPDXRef-DOCUMENT",
+                                            comment="This relationship has a comment."),
+                               Relationship(related_spdx_element_id="SPDXRef-DOCUMENT",
+                                            relationship_type=RelationshipType.DESCRIBED_BY,
+                                            spdx_element_id="SPDXRef-File",
+                                            comment="This relationship has a comment.")]),
+                          (["SPDXRef-Package", "SPDXRef-File", "SPDXRef-Package"], [], [
+                              Relationship(related_spdx_element_id="SPDXRef-Package",
+                                           relationship_type=RelationshipType.DESCRIBES,
+                                           spdx_element_id="SPDXRef-DOCUMENT"),
+                              Relationship(related_spdx_element_id="SPDXRef-File",
+                                           relationship_type=RelationshipType.DESCRIBES,
+                                           spdx_element_id="SPDXRef-DOCUMENT")])])
+def test_parse_document_describes_without_duplicating_relationships(document_describes, relationships,
+                                                                    parsed_relationships):
     relationship_parser = RelationshipParser()
     document_dict = {
         "SPDXID": "SPDXRef-DOCUMENT",
-        "documentDescribes": ["SPDXRef-Package", "SPDXRef-File"],
-        "relationships": [{"spdxElementId": "SPDXRef-DOCUMENT", "relatedSpdxElement": "SPDXRef-Package",
-                           "relationshipType": "DESCRIBES",
-                           "comment": "This relationship has a comment."},
-                          {"spdxElementId": "SPDXRef-File", "relatedSpdxElement": "SPDXRef-DOCUMENT",
-                           "relationshipType": "DESCRIBED_BY", "comment": "This relationship has a comment."}
-                          ]}
+        "documentDescribes": document_describes,
+        "relationships": relationships}
 
     relationships = relationship_parser.parse_all_relationships(document_dict)
 
-    assert len(relationships) == 2
-    TestCase().assertCountEqual(relationships, [
-        Relationship(related_spdx_element_id="SPDXRef-Package", relationship_type=RelationshipType.DESCRIBES,
-                     spdx_element_id="SPDXRef-DOCUMENT", comment="This relationship has a comment."),
-        Relationship(related_spdx_element_id="SPDXRef-DOCUMENT", relationship_type=RelationshipType.DESCRIBED_BY,
-                     spdx_element_id="SPDXRef-File", comment="This relationship has a comment.")])
+    assert len(relationships) == len(parsed_relationships)
+    TestCase().assertCountEqual(relationships, parsed_relationships)
 
 
 def test_parse_has_files():
@@ -121,22 +134,34 @@ def test_parse_has_files():
                      related_spdx_element_id="SPDXRef-File2")])
 
 
-def test_parse_has_files_without_duplicating_relationships():
+@pytest.mark.parametrize("has_files,existing_relationships,contains_relationships",
+                         [(["SPDXRef-File1", "SPDXRef-File2"], [
+                             Relationship(spdx_element_id="SPDXRef-Package",
+                                          relationship_type=RelationshipType.CONTAINS,
+                                          related_spdx_element_id="SPDXRef-File1",
+                                          comment="This relationship has a comment."),
+                             Relationship(spdx_element_id="SPDXRef-File2",
+                                          relationship_type=RelationshipType.CONTAINED_BY,
+                                          related_spdx_element_id="SPDXRef-Package")], []),
+                          (["SPDXRef-File1", "SPDXRef-File2", "SPDXRef-File1"], [], [
+                              Relationship(spdx_element_id="SPDXRef-Package",
+                                          relationship_type=RelationshipType.CONTAINS,
+                                          related_spdx_element_id="SPDXRef-File1"),
+                              Relationship(spdx_element_id="SPDXRef-Package",
+                                          relationship_type=RelationshipType.CONTAINS,
+                                          related_spdx_element_id="SPDXRef-File2")])])
+def test_parse_has_files_without_duplicating_relationships(has_files, existing_relationships,
+                                                           contains_relationships):
     relationship_parser = RelationshipParser()
     document_dict = {
         "packages":
             [{
                 "SPDXID": "SPDXRef-Package",
-                "hasFiles": ["SPDXRef-File1", "SPDXRef-File2"]
+                "hasFiles": has_files
             }]
     }
-    existing_relationships = [
-        Relationship(spdx_element_id="SPDXRef-Package", relationship_type=RelationshipType.CONTAINS,
-                     related_spdx_element_id="SPDXRef-File1", comment="This relationship has a comment."),
-        Relationship(spdx_element_id="SPDXRef-File2", relationship_type=RelationshipType.CONTAINED_BY,
-                     related_spdx_element_id="SPDXRef-Package")]
-
     relationships = relationship_parser.parse_has_files(document_dict.get("packages"),
                                                         existing_relationships=existing_relationships)
 
-    assert len(relationships) == 0
+    assert len(relationships) == len(contains_relationships)
+    TestCase().assertCountEqual(relationships, contains_relationships)
diff --git a/tests/parser/jsonlikedict/test_snippet_parser.py b/tests/parser/jsonlikedict/test_snippet_parser.py
index e9108ee..1fe87ae 100644
--- a/tests/parser/jsonlikedict/test_snippet_parser.py
+++ b/tests/parser/jsonlikedict/test_snippet_parser.py
@@ -61,7 +61,7 @@ def test_parse_snippet():
     assert snippet.line_range == (5, 23)
     assert snippet.file_spdx_id == "SPDXRef-DoapSource"
     assert snippet.license_info_in_snippet == [LicenseExpression("GPL-2.0-only")]
-    assert snippet.concluded_license == LicenseExpression("GPL-2.0-only")
+    assert snippet.license_concluded == LicenseExpression("GPL-2.0-only")
     assert snippet.attribution_texts == ["Some example attibution text."]
 
 
diff --git a/tests/writer/json/expected_results/expected.json b/tests/writer/json/expected_results/expected.json
index ee4b8e9..5277817 100644
--- a/tests/writer/json/expected_results/expected.json
+++ b/tests/writer/json/expected_results/expected.json
@@ -54,7 +54,7 @@
                 "TEXT"
             ],
             "licenseComments": "licenseComment",
-            "licenseConcluded": "concludedLicenseExpression",
+            "licenseConcluded": "licenseConcludedExpression",
             "licenseInfoInFiles": [
                 "licenseInfoInFileExpression"
             ],