diff --git a/cyclonedx/__init__.py b/cyclonedx/__init__.py
index b56627fc..de60e8e9 100644
--- a/cyclonedx/__init__.py
+++ b/cyclonedx/__init__.py
@@ -22,4 +22,4 @@
 
 # !! version is managed by semantic_release
 # do not use typing here, or else `semantic_release` might have issues finding the variable
-__version__ = "8.9.0"  # noqa:Q000
+__version__ = "9.0.1-rc.1"  # noqa:Q000
diff --git a/cyclonedx/_internal/compare.py b/cyclonedx/_internal/compare.py
index 226fa615..bd64e692 100644
--- a/cyclonedx/_internal/compare.py
+++ b/cyclonedx/_internal/compare.py
@@ -42,7 +42,7 @@ def __lt__(self, other: Any) -> bool:
                 return False
             if o is None:
                 return True
-            return True if s < o else False
+            return bool(s < o)
         return False
 
     def __gt__(self, other: Any) -> bool:
@@ -54,31 +54,17 @@ def __gt__(self, other: Any) -> bool:
                 return True
             if o is None:
                 return False
-            return True if s > o else False
+            return bool(s > o)
         return False
 
 
-class ComparableDict:
+class ComparableDict(ComparableTuple):
     """
     Allows comparison of dictionaries, allowing for missing/None values.
     """
 
-    def __init__(self, dict_: Dict[Any, Any]) -> None:
-        self._dict = dict_
-
-    def __lt__(self, other: Any) -> bool:
-        if not isinstance(other, ComparableDict):
-            return True
-        keys = sorted(self._dict.keys() | other._dict.keys())
-        return ComparableTuple(self._dict.get(k) for k in keys) \
-            < ComparableTuple(other._dict.get(k) for k in keys)
-
-    def __gt__(self, other: Any) -> bool:
-        if not isinstance(other, ComparableDict):
-            return False
-        keys = sorted(self._dict.keys() | other._dict.keys())
-        return ComparableTuple(self._dict.get(k) for k in keys) \
-            > ComparableTuple(other._dict.get(k) for k in keys)
+    def __new__(cls, d: Dict[Any, Any]) -> 'ComparableDict':
+        return super(ComparableDict, cls).__new__(cls, sorted(d.items()))
 
 
 class ComparablePackageURL(ComparableTuple):
@@ -86,12 +72,11 @@ class ComparablePackageURL(ComparableTuple):
     Allows comparison of PackageURL, allowing for qualifiers.
     """
 
-    def __new__(cls, purl: 'PackageURL') -> 'ComparablePackageURL':
-        return super().__new__(
-            ComparablePackageURL, (
-                purl.type,
-                purl.namespace,
-                purl.version,
-                ComparableDict(purl.qualifiers) if isinstance(purl.qualifiers, dict) else purl.qualifiers,
-                purl.subpath
-            ))
+    def __new__(cls, p: 'PackageURL') -> 'ComparablePackageURL':
+        return super(ComparablePackageURL, cls).__new__(cls, (
+            p.type,
+            p.namespace,
+            p.version,
+            ComparableDict(p.qualifiers) if isinstance(p.qualifiers, dict) else p.qualifiers,
+            p.subpath
+        ))
diff --git a/cyclonedx/factory/license.py b/cyclonedx/factory/license.py
index f96cb697..40d4484d 100644
--- a/cyclonedx/factory/license.py
+++ b/cyclonedx/factory/license.py
@@ -19,7 +19,7 @@
 
 from ..exception.factory import InvalidLicenseExpressionException, InvalidSpdxLicenseException
 from ..model.license import DisjunctiveLicense, LicenseExpression
-from ..spdx import fixup_id as spdx_fixup, is_compound_expression as is_spdx_compound_expression
+from ..spdx import fixup_id as spdx_fixup, is_expression as is_spdx_expression
 
 if TYPE_CHECKING:  # pragma: no cover
     from ..model import AttachedText, XsUri
@@ -57,11 +57,11 @@ def make_with_expression(self, expression: str, *,
                              ) -> LicenseExpression:
         """Make a :class:`cyclonedx.model.license.LicenseExpression` with a compound expression.
 
-        Utilizes :func:`cyclonedx.spdx.is_compound_expression`.
+        Utilizes :func:`cyclonedx.spdx.is_expression`.
 
         :raises InvalidLicenseExpressionException: if param `value` is not known/supported license expression
         """
-        if is_spdx_compound_expression(expression):
+        if is_spdx_expression(expression):
             return LicenseExpression(expression, acknowledgement=acknowledgement)
         raise InvalidLicenseExpressionException(expression)
 
diff --git a/cyclonedx/model/__init__.py b/cyclonedx/model/__init__.py
index 3a041b20..b2e40187 100644
--- a/cyclonedx/model/__init__.py
+++ b/cyclonedx/model/__init__.py
@@ -33,7 +33,7 @@
 from warnings import warn
 from xml.etree.ElementTree import Element as XmlElement  # nosec B405
 
-import serializable
+import py_serializable as serializable
 from sortedcontainers import SortedSet
 
 from .._internal.compare import ComparableTuple as _ComparableTuple
@@ -128,22 +128,23 @@ def classification(self) -> str:
     def classification(self, classification: str) -> None:
         self._classification = classification
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.flow, self.classification
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, DataClassification):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: object) -> bool:
         if isinstance(other, DataClassification):
-            return _ComparableTuple((
-                self.flow, self.classification
-            )) < _ComparableTuple((
-                other.flow, other.classification
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.flow, self.classification))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<DataClassification flow={self.flow}>'
@@ -231,22 +232,23 @@ def content(self) -> str:
     def content(self, content: str) -> None:
         self._content = content
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.content_type, self.encoding, self.content,
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, AttachedText):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, AttachedText):
-            return _ComparableTuple((
-                self.content_type, self.content, self.encoding
-            )) < _ComparableTuple((
-                other.content_type, other.content, other.encoding
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.content, self.content_type, self.encoding))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<AttachedText content-type={self.content_type}, encoding={self.encoding}>'
@@ -510,22 +512,23 @@ def content(self) -> str:
     def content(self, content: str) -> None:
         self._content = content
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.alg, self.content
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, HashType):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, HashType):
-            return _ComparableTuple((
-                self.alg, self.content
-            )) < _ComparableTuple((
-                other.alg, other.content
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.alg, self.content))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<HashType {self.alg.name}:{self.content}>'
@@ -728,7 +731,7 @@ def __init__(self, uri: str) -> None:
 
     def __eq__(self, other: Any) -> bool:
         if isinstance(other, XsUri):
-            return hash(other) == hash(self)
+            return self._uri == other._uri
         return False
 
     def __lt__(self, other: Any) -> bool:
@@ -887,25 +890,24 @@ def hashes(self) -> 'SortedSet[HashType]':
     def hashes(self, hashes: Iterable[HashType]) -> None:
         self._hashes = SortedSet(hashes)
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self._type, self._url, self._comment,
+            _ComparableTuple(self._hashes)
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, ExternalReference):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, ExternalReference):
-            return _ComparableTuple((
-                self._type, self._url, self._comment
-            )) < _ComparableTuple((
-                other._type, other._url, other._comment
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((
-            self._type, self._url, self._comment,
-            tuple(sorted(self._hashes, key=hash))
-        ))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<ExternalReference {self.type.name}, {self.url}>'
@@ -964,22 +966,23 @@ def value(self) -> Optional[str]:
     def value(self, value: Optional[str]) -> None:
         self._value = value
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.name, self.value
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, Property):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, Property):
-            return _ComparableTuple((
-                self.name, self.value
-            )) < _ComparableTuple((
-                other.name, other.value
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.name, self.value))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<Property name={self.name}>'
@@ -1055,22 +1058,23 @@ def encoding(self) -> Optional[Encoding]:
     def encoding(self, encoding: Optional[Encoding]) -> None:
         self._encoding = encoding
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.content, self.content_type, self.encoding
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, NoteText):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, NoteText):
-            return _ComparableTuple((
-                self.content, self.content_type, self.encoding
-            )) < _ComparableTuple((
-                other.content, other.content_type, other.encoding
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.content, self.content_type, self.encoding))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<NoteText content_type={self.content_type}, encoding={self.encoding}>'
@@ -1139,22 +1143,23 @@ def locale(self, locale: Optional[str]) -> None:
                     " ISO-3166 (or higher) country code. according to ISO-639 format. Examples include: 'en', 'en-US'."
                 )
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.locale, self.text
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, Note):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, Note):
-            return _ComparableTuple((
-                self.locale, self.text
-            )) < _ComparableTuple((
-                other.locale, other.text
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.text, self.locale))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<Note id={id(self)}, locale={self.locale}>'
@@ -1224,22 +1229,23 @@ def email(self) -> Optional[str]:
     def email(self, email: Optional[str]) -> None:
         self._email = email
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.timestamp, self.name, self.email
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, IdentifiableAction):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, IdentifiableAction):
-            return _ComparableTuple((
-                self.timestamp, self.name, self.email
-            )) < _ComparableTuple((
-                other.timestamp, other.name, other.email
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.timestamp, self.name, self.email))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<IdentifiableAction name={self.name}, email={self.email}>'
@@ -1277,16 +1283,16 @@ def text(self, text: str) -> None:
 
     def __eq__(self, other: object) -> bool:
         if isinstance(other, Copyright):
-            return hash(other) == hash(self)
+            return self._text == other._text
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, Copyright):
-            return self.text < other.text
+            return self._text < other._text
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash(self.text)
+        return hash(self._text)
 
     def __repr__(self) -> str:
         return f'<Copyright text={self.text}>'
diff --git a/cyclonedx/model/bom.py b/cyclonedx/model/bom.py
index 8a8d7405..130074ee 100644
--- a/cyclonedx/model/bom.py
+++ b/cyclonedx/model/bom.py
@@ -22,9 +22,10 @@
 from uuid import UUID, uuid4
 from warnings import warn
 
-import serializable
+import py_serializable as serializable
 from sortedcontainers import SortedSet
 
+from .._internal.compare import ComparableTuple as _ComparableTuple
 from .._internal.time import get_now_utc as _get_now_utc
 from ..exception.model import LicenseExpressionAlongWithOthersException, UnknownComponentDependencyException
 from ..schema.schema import (
@@ -293,16 +294,20 @@ def properties(self) -> 'SortedSet[Property]':
     def properties(self, properties: Iterable[Property]) -> None:
         self._properties = SortedSet(properties)
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            _ComparableTuple(self.authors), self.component, _ComparableTuple(self.licenses), self.manufacture,
+            _ComparableTuple(self.properties),
+            _ComparableTuple(self.lifecycles), self.supplier, self.timestamp, self.tools, self.manufacturer
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, BomMetaData):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __hash__(self) -> int:
-        return hash((
-            tuple(self.authors), self.component, tuple(self.licenses), self.manufacture, tuple(self.properties),
-            tuple(self.lifecycles), self.supplier, self.timestamp, self.tools, self.manufacturer
-        ))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<BomMetaData timestamp={self.timestamp}, component={self.component}>'
@@ -722,17 +727,22 @@ def validate(self) -> bool:
 
         return True
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.serial_number, self.version, self.metadata, _ComparableTuple(
+                self.components), _ComparableTuple(self.services),
+            _ComparableTuple(self.external_references), _ComparableTuple(
+                self.dependencies), _ComparableTuple(self.properties),
+            _ComparableTuple(self.vulnerabilities),
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, Bom):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __hash__(self) -> int:
-        return hash((
-            self.serial_number, self.version, self.metadata, tuple(self.components), tuple(self.services),
-            tuple(self.external_references), tuple(self.dependencies), tuple(self.properties),
-            tuple(self.vulnerabilities),
-        ))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<Bom uuid={self.serial_number}, hash={hash(self)}>'
diff --git a/cyclonedx/model/bom_ref.py b/cyclonedx/model/bom_ref.py
index 85bcf501..cc4571a7 100644
--- a/cyclonedx/model/bom_ref.py
+++ b/cyclonedx/model/bom_ref.py
@@ -18,7 +18,7 @@
 
 from typing import TYPE_CHECKING, Any, Optional
 
-import serializable
+import py_serializable as serializable
 
 from ..exception.serialization import CycloneDxDeserializationException, SerializationOfUnexpectedValueException
 
diff --git a/cyclonedx/model/component.py b/cyclonedx/model/component.py
index df072597..28de1ea0 100644
--- a/cyclonedx/model/component.py
+++ b/cyclonedx/model/component.py
@@ -22,7 +22,7 @@
 from warnings import warn
 
 # See https://github.com/package-url/packageurl-python/issues/65
-import serializable
+import py_serializable as serializable
 from packageurl import PackageURL
 from sortedcontainers import SortedSet
 
@@ -166,22 +166,25 @@ def message(self) -> Optional[str]:
     def message(self, message: Optional[str]) -> None:
         self._message = message
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.uid, self.url,
+            self.author, self.committer,
+            self.message
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, Commit):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, Commit):
-            return _ComparableTuple((
-                self.uid, self.url, self.author, self.committer, self.message
-            )) < _ComparableTuple((
-                other.uid, other.url, other.author, other.committer, other.message
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.uid, self.url, self.author, self.committer, self.message))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<Commit uid={self.uid}, url={self.url}, message={self.message}>'
@@ -271,13 +274,19 @@ def copyright(self) -> 'SortedSet[Copyright]':
     def copyright(self, copyright: Iterable[Copyright]) -> None:
         self._copyright = SortedSet(copyright)
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            _ComparableTuple(self.licenses),
+            _ComparableTuple(self.copyright),
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, ComponentEvidence):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __hash__(self) -> int:
-        return hash((tuple(self.licenses), tuple(self.copyright)))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<ComponentEvidence id={id(self)}>'
@@ -463,22 +472,24 @@ def url(self) -> Optional[XsUri]:
     def url(self, url: Optional[XsUri]) -> None:
         self._url = url
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.url,
+            self.text,
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, Diff):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, Diff):
-            return _ComparableTuple((
-                self.url, self.text
-            )) < _ComparableTuple((
-                other.url, other.text
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.text, self.url))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<Diff url={self.url}>'
@@ -565,22 +576,24 @@ def resolves(self) -> 'SortedSet[IssueType]':
     def resolves(self, resolves: Iterable[IssueType]) -> None:
         self._resolves = SortedSet(resolves)
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.type, self.diff,
+            _ComparableTuple(self.resolves)
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, Patch):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, Patch):
-            return _ComparableTuple((
-                self.type, self.diff, _ComparableTuple(self.resolves)
-            )) < _ComparableTuple((
-                other.type, other.diff, _ComparableTuple(other.resolves)
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.type, self.diff, tuple(self.resolves)))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<Patch type={self.type}, id={id(self)}>'
@@ -727,16 +740,23 @@ def notes(self) -> Optional[str]:
     def notes(self, notes: Optional[str]) -> None:
         self._notes = notes
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            _ComparableTuple(self.ancestors),
+            _ComparableTuple(self.descendants),
+            _ComparableTuple(self.variants),
+            _ComparableTuple(self.commits),
+            _ComparableTuple(self.patches),
+            self.notes
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, Pedigree):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __hash__(self) -> int:
-        return hash((
-            tuple(self.ancestors), tuple(self.descendants), tuple(self.variants), tuple(self.commits),
-            tuple(self.patches), self.notes
-        ))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<Pedigree id={id(self)}, hash={hash(self)}>'
@@ -872,13 +892,23 @@ def url(self) -> Optional[XsUri]:
     def url(self, url: Optional[XsUri]) -> None:
         self._url = url
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.tag_id,
+            self.name, self.version,
+            self.tag_version,
+            self.patch,
+            self.url,
+            self.text,
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, Swid):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __hash__(self) -> int:
-        return hash((self.tag_id, self.name, self.version, self.tag_version, self.patch, self.text, self.url))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<Swid tagId={self.tag_id}, name={self.name}, version={self.version}>'
@@ -925,7 +955,7 @@ def deserialize(cls, o: Any) -> 'OmniborId':
 
     def __eq__(self, other: Any) -> bool:
         if isinstance(other, OmniborId):
-            return hash(other) == hash(self)
+            return self._id == other._id
         return False
 
     def __lt__(self, other: Any) -> bool:
@@ -984,7 +1014,7 @@ def deserialize(cls, o: Any) -> 'Swhid':
 
     def __eq__(self, other: Any) -> bool:
         if isinstance(other, Swhid):
-            return hash(other) == hash(self)
+            return self._id == other._id
         return False
 
     def __lt__(self, other: Any) -> bool:
@@ -1720,51 +1750,35 @@ def get_pypi_url(self) -> str:
         else:
             return f'https://pypi.org/project/{self.name}'
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.type, self.group, self.name, self.version,
+            self.bom_ref.value,
+            None if self.purl is None else _ComparablePackageURL(self.purl),
+            self.swid, self.cpe, _ComparableTuple(self.swhids),
+            self.supplier, self.author, self.publisher,
+            self.description,
+            self.mime_type, self.scope, _ComparableTuple(self.hashes),
+            _ComparableTuple(self.licenses), self.copyright,
+            self.pedigree,
+            _ComparableTuple(self.external_references), _ComparableTuple(self.properties),
+            _ComparableTuple(self.components), self.evidence, self.release_notes, self.modified,
+            _ComparableTuple(self.authors), _ComparableTuple(self.omnibor_ids), self.manufacturer,
+            self.crypto_properties, _ComparableTuple(self.tags),
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, Component):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, Component):
-            return _ComparableTuple((
-                self.type, self.group, self.name, self.version,
-                self.mime_type, self.supplier, self.author, self.publisher,
-                self.description, self.scope, _ComparableTuple(self.hashes),
-                _ComparableTuple(self.licenses), self.copyright, self.cpe,
-                None if self.purl is None else _ComparablePackageURL(self.purl),
-                self.swid, self.pedigree,
-                _ComparableTuple(self.external_references), _ComparableTuple(self.properties),
-                _ComparableTuple(self.components), self.evidence, self.release_notes, self.modified,
-                _ComparableTuple(self.authors), _ComparableTuple(self.omnibor_ids), self.manufacturer,
-                _ComparableTuple(self.swhids), self.crypto_properties, _ComparableTuple(self.tags)
-            )) < _ComparableTuple((
-                other.type, other.group, other.name, other.version,
-                other.mime_type, other.supplier, other.author, other.publisher,
-                other.description, other.scope, _ComparableTuple(other.hashes),
-                _ComparableTuple(other.licenses), other.copyright, other.cpe,
-                None if other.purl is None else _ComparablePackageURL(other.purl),
-                other.swid, other.pedigree,
-                _ComparableTuple(other.external_references), _ComparableTuple(other.properties),
-                _ComparableTuple(other.components), other.evidence, other.release_notes, other.modified,
-                _ComparableTuple(other.authors), _ComparableTuple(other.omnibor_ids), other.manufacturer,
-                _ComparableTuple(other.swhids), other.crypto_properties, _ComparableTuple(other.tags)
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((
-            self.type, self.group, self.name, self.version,
-            self.mime_type, self.supplier, self.author, self.publisher,
-            self.description, self.scope, tuple(self.hashes),
-            tuple(self.licenses), self.copyright, self.cpe,
-            self.purl,
-            self.swid, self.pedigree,
-            tuple(self.external_references), tuple(self.properties),
-            tuple(self.components), self.evidence, self.release_notes, self.modified,
-            tuple(self.authors), tuple(self.omnibor_ids), self.manufacturer,
-            tuple(self.swhids), self.crypto_properties, tuple(self.tags)
-        ))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<Component bom-ref={self.bom_ref!r}, group={self.group}, name={self.name}, ' \
diff --git a/cyclonedx/model/contact.py b/cyclonedx/model/contact.py
index 299e8c06..cea865e7 100644
--- a/cyclonedx/model/contact.py
+++ b/cyclonedx/model/contact.py
@@ -18,7 +18,7 @@
 
 from typing import Any, Iterable, Optional, Union
 
-import serializable
+import py_serializable as serializable
 from sortedcontainers import SortedSet
 
 from .._internal.bom_ref import bom_ref_from_str as _bom_ref_from_str
@@ -161,25 +161,26 @@ def street_address(self) -> Optional[str]:
     def street_address(self, street_address: Optional[str]) -> None:
         self._street_address = street_address
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.country, self.region, self.locality, self.postal_code,
+            self.post_office_box_number,
+            self.street_address,
+            None if self.bom_ref is None else self.bom_ref.value,
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, PostalAddress):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, PostalAddress):
-            return _ComparableTuple((
-                self.bom_ref, self.country, self.region, self.locality, self.post_office_box_number, self.postal_code,
-                self.street_address
-            )) < _ComparableTuple((
-                other.bom_ref, other.country, other.region, other.locality, other.post_office_box_number,
-                other.postal_code, other.street_address
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.bom_ref, self.country, self.region, self.locality, self.post_office_box_number,
-                     self.postal_code, self.street_address))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<PostalAddress bom-ref={self.bom_ref}, street_address={self.street_address}, country={self.country}>'
@@ -253,22 +254,23 @@ def phone(self) -> Optional[str]:
     def phone(self, phone: Optional[str]) -> None:
         self._phone = phone
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.name, self.email, self.phone
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, OrganizationalContact):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, OrganizationalContact):
-            return _ComparableTuple((
-                self.name, self.email, self.phone
-            )) < _ComparableTuple((
-                other.name, other.email, other.phone
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.name, self.phone, self.email))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<OrganizationalContact name={self.name}, email={self.email}, phone={self.phone}>'
@@ -362,18 +364,23 @@ def contacts(self) -> 'SortedSet[OrganizationalContact]':
     def contacts(self, contacts: Iterable[OrganizationalContact]) -> None:
         self._contacts = SortedSet(contacts)
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.name, _ComparableTuple(self.urls), _ComparableTuple(self.contacts)
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, OrganizationalEntity):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, OrganizationalEntity):
-            return hash(self) < hash(other)
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.name, tuple(self.urls), tuple(self.contacts)))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<OrganizationalEntity name={self.name}>'
diff --git a/cyclonedx/model/crypto.py b/cyclonedx/model/crypto.py
index b1875f53..765e840b 100644
--- a/cyclonedx/model/crypto.py
+++ b/cyclonedx/model/crypto.py
@@ -29,7 +29,7 @@
 from enum import Enum
 from typing import Any, Iterable, Optional
 
-import serializable
+import py_serializable as serializable
 from sortedcontainers import SortedSet
 
 from .._internal.compare import ComparableTuple as _ComparableTuple
@@ -494,15 +494,20 @@ def nist_quantum_security_level(self, nist_quantum_security_level: Optional[int]
             )
         self._nist_quantum_security_level = nist_quantum_security_level
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.primitive, self._parameter_set_identifier, self.curve, self.execution_environment,
+            self.implementation_platform, _ComparableTuple(self.certification_levels), self.mode, self.padding,
+            _ComparableTuple(self.crypto_functions), self.classical_security_level, self.nist_quantum_security_level,
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, AlgorithmProperties):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __hash__(self) -> int:
-        return hash((self.primitive, self._parameter_set_identifier, self.curve, self.execution_environment,
-                     self.implementation_platform, tuple(self.certification_levels), self.mode, self.padding,
-                     tuple(self.crypto_functions), self.classical_security_level, self.nist_quantum_security_level,))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<AlgorithmProperties primitive={self.primitive}, execution_environment={self.execution_environment}>'
@@ -666,14 +671,19 @@ def certificate_extension(self) -> Optional[str]:
     def certificate_extension(self, certificate_extension: Optional[str]) -> None:
         self._certificate_extension = certificate_extension
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.subject_name, self.issuer_name, self.not_valid_before, self.not_valid_after,
+            self.certificate_format, self.certificate_extension
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, CertificateProperties):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __hash__(self) -> int:
-        return hash((self.subject_name, self.issuer_name, self.not_valid_before, self.not_valid_after,
-                     self.certificate_format, self.certificate_extension))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<CertificateProperties subject_name={self.subject_name}, certificate_format={self.certificate_format}>'
@@ -789,13 +799,18 @@ def algorithm_ref(self) -> Optional[BomRef]:
     def algorithm_ref(self, algorithm_ref: Optional[BomRef]) -> None:
         self._algorithm_ref = algorithm_ref
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.mechanism, self.algorithm_ref
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, RelatedCryptoMaterialSecuredBy):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __hash__(self) -> int:
-        return hash((self.mechanism, self.algorithm_ref))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<RelatedCryptoMaterialSecuredBy mechanism={self.mechanism}, algorithm_ref={self.algorithm_ref}>'
@@ -1028,14 +1043,19 @@ def secured_by(self) -> Optional[RelatedCryptoMaterialSecuredBy]:
     def secured_by(self, secured_by: Optional[RelatedCryptoMaterialSecuredBy]) -> None:
         self._secured_by = secured_by
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.type, self.id, self.state, self.algorithm_ref, self.creation_date, self.activation_date,
+            self.update_date, self.expiration_date, self.value, self.size, self.format, self.secured_by
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, RelatedCryptoMaterialProperties):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __hash__(self) -> int:
-        return hash((self.type, self.id, self.state, self.algorithm_ref, self.creation_date, self.activation_date,
-                     self.update_date, self.expiration_date, self.value, self.size, self.format, self.secured_by))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<RelatedCryptoMaterialProperties type={self.type}, id={self.id} state={self.state}>'
@@ -1136,22 +1156,23 @@ def identifiers(self) -> 'SortedSet[str]':
     def identifiers(self, identifiers: Iterable[str]) -> None:
         self._identifiers = SortedSet(identifiers)
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.name, _ComparableTuple(self.algorithms), _ComparableTuple(self.identifiers)
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, ProtocolPropertiesCipherSuite):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, ProtocolPropertiesCipherSuite):
-            return _ComparableTuple((
-                self.name, _ComparableTuple(self.algorithms), _ComparableTuple(self.identifiers)
-            )) < _ComparableTuple((
-                other.name, _ComparableTuple(other.algorithms), _ComparableTuple(other.identifiers)
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.name, tuple(self.algorithms), tuple(self.identifiers)))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<ProtocolPropertiesCipherSuite name={self.name}>'
@@ -1277,13 +1298,23 @@ def auth(self) -> 'SortedSet[BomRef]':
     def auth(self, auth: Iterable[BomRef]) -> None:
         self._auth = SortedSet(auth)
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            _ComparableTuple(self.encr),
+            _ComparableTuple(self.prf),
+            _ComparableTuple(self.integ),
+            _ComparableTuple(self.ke),
+            self.esn,
+            _ComparableTuple(self.auth)
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, Ikev2TransformTypes):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __hash__(self) -> int:
-        return hash((tuple(self.encr), tuple(self.prf), tuple(self.integ), tuple(self.ke), self.esn, tuple(self.auth)))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<Ikev2TransformTypes esn={self.esn}>'
@@ -1394,21 +1425,22 @@ def crypto_refs(self) -> 'SortedSet[BomRef]':
     def crypto_refs(self, crypto_refs: Iterable[BomRef]) -> None:
         self._crypto_refs = SortedSet(crypto_refs)
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.type,
+            self.version,
+            _ComparableTuple(self.cipher_suites),
+            self.ikev2_transform_types,
+            _ComparableTuple(self.crypto_refs)
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, ProtocolProperties):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __hash__(self) -> int:
-        return hash(
-            (
-                self.type,
-                self.version,
-                tuple(self.cipher_suites),
-                self.ikev2_transform_types,
-                tuple(self.crypto_refs)
-            )
-        )
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<ProtocolProperties type={self.type}, version={self.version}>'
@@ -1539,33 +1571,28 @@ def oid(self) -> Optional[str]:
     def oid(self, oid: Optional[str]) -> None:
         self._oid = oid
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.asset_type,
+            self.algorithm_properties,
+            self.certificate_properties,
+            self.related_crypto_material_properties,
+            self.protocol_properties,
+            self.oid,
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, CryptoProperties):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, CryptoProperties):
-            return _ComparableTuple((
-                self.asset_type,
-                self.algorithm_properties,
-                self.certificate_properties,
-                self.related_crypto_material_properties,
-                self.protocol_properties,
-                self.oid,
-            )) < _ComparableTuple((
-                other.asset_type,
-                other.algorithm_properties,
-                other.certificate_properties,
-                other.related_crypto_material_properties,
-                other.protocol_properties,
-                other.oid,
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.asset_type, self.algorithm_properties, self.certificate_properties,
-                     self.related_crypto_material_properties, self.protocol_properties, self.oid))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<CryptoProperties asset_type={self.asset_type}, oid={self.oid}>'
diff --git a/cyclonedx/model/definition.py b/cyclonedx/model/definition.py
index 90872e32..675e5476 100644
--- a/cyclonedx/model/definition.py
+++ b/cyclonedx/model/definition.py
@@ -18,7 +18,7 @@
 import re
 from typing import TYPE_CHECKING, Any, Iterable, Optional, Union
 
-import serializable
+import py_serializable as serializable
 from sortedcontainers import SortedSet
 
 from .._internal.bom_ref import bom_ref_from_str as _bom_ref_from_str
@@ -256,7 +256,7 @@ def external_references(self, external_references: Iterable[ExternalReference])
     def __comparable_tuple(self) -> _ComparableTuple:
         # all properties are optional - so need to compare all, in hope that one is unique
         return _ComparableTuple((
-            self.bom_ref, self.identifier,
+            self.identifier, self.bom_ref.value,
             self.title, self.text,
             _ComparableTuple(self.descriptions),
             _ComparableTuple(self.open_cre), self.parent, _ComparableTuple(self.properties),
@@ -373,7 +373,9 @@ def requirements(self, requirements: Iterable[Union[str, BomRef]]) -> None:
     def __comparable_tuple(self) -> _ComparableTuple:
         # all properties are optional - so need to compare all, in hope that one is unique
         return _ComparableTuple((
-            self.bom_ref, self.identifier, self.title, self.description, _ComparableTuple(self.requirements)
+            self.identifier, self.bom_ref.value,
+            self.title, self.description,
+            _ComparableTuple(self.requirements)
         ))
 
     def __lt__(self, other: Any) -> bool:
@@ -545,8 +547,9 @@ def external_references(self, external_references: Iterable[ExternalReference])
     def __comparable_tuple(self) -> _ComparableTuple:
         # all properties are optional - so need to apply all, in hope that one is unique
         return _ComparableTuple((
-            self.bom_ref,
-            self.name, self.version, self.description, self.owner,
+            self.name, self.version,
+            self.bom_ref.value,
+            self.description, self.owner,
             _ComparableTuple(self.requirements), _ComparableTuple(self.levels),
             _ComparableTuple(self.external_references)
         ))
@@ -608,13 +611,13 @@ def __eq__(self, other: object) -> bool:
             return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
-    def __hash__(self) -> int:
-        return hash(self.__comparable_tuple())
-
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, Definitions):
             return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
+    def __hash__(self) -> int:
+        return hash(self.__comparable_tuple())
+
     def __repr__(self) -> str:
         return f'<Definitions standards={self.standards!r} >'
diff --git a/cyclonedx/model/dependency.py b/cyclonedx/model/dependency.py
index 0b054c37..8241fdfc 100644
--- a/cyclonedx/model/dependency.py
+++ b/cyclonedx/model/dependency.py
@@ -19,7 +19,7 @@
 from abc import ABC, abstractmethod
 from typing import Any, Iterable, List, Optional, Set
 
-import serializable
+import py_serializable as serializable
 from sortedcontainers import SortedSet
 
 from .._internal.compare import ComparableTuple as _ComparableTuple
@@ -83,22 +83,23 @@ def dependencies(self, dependencies: Iterable['Dependency']) -> None:
     def dependencies_as_bom_refs(self) -> Set[BomRef]:
         return set(map(lambda d: d.ref, self.dependencies))
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.ref, _ComparableTuple(self.dependencies)
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, Dependency):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, Dependency):
-            return _ComparableTuple((
-                self.ref, _ComparableTuple(self.dependencies)
-            )) < _ComparableTuple((
-                other.ref, _ComparableTuple(other.dependencies)
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.ref, tuple(self.dependencies)))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<Dependency ref={self.ref!r}, targets={len(self.dependencies)}>'
diff --git a/cyclonedx/model/impact_analysis.py b/cyclonedx/model/impact_analysis.py
index 6722efaf..a289daf2 100644
--- a/cyclonedx/model/impact_analysis.py
+++ b/cyclonedx/model/impact_analysis.py
@@ -28,7 +28,7 @@
 
 from enum import Enum
 
-import serializable
+import py_serializable as serializable
 
 
 @serializable.serializable_enum
diff --git a/cyclonedx/model/issue.py b/cyclonedx/model/issue.py
index b0886d63..4b1f1aa2 100644
--- a/cyclonedx/model/issue.py
+++ b/cyclonedx/model/issue.py
@@ -18,7 +18,7 @@
 from enum import Enum
 from typing import Any, Iterable, Optional
 
-import serializable
+import py_serializable as serializable
 from sortedcontainers import SortedSet
 
 from .._internal.compare import ComparableTuple as _ComparableTuple
@@ -85,22 +85,23 @@ def url(self) -> Optional[XsUri]:
     def url(self, url: Optional[XsUri]) -> None:
         self._url = url
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.name, self.url
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, IssueTypeSource):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, IssueTypeSource):
-            return _ComparableTuple((
-                self.name, self.url
-            )) < _ComparableTuple((
-                other.name, other.url
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.name, self.url))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<IssueTypeSource name={self._name}, url={self.url}>'
@@ -226,24 +227,24 @@ def references(self) -> 'SortedSet[XsUri]':
     def references(self, references: Iterable[XsUri]) -> None:
         self._references = SortedSet(references)
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.type, self.id, self.name, self.description, self.source,
+            _ComparableTuple(self.references)
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, IssueType):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, IssueType):
-            return _ComparableTuple((
-                self.type, self.id, self.name, self.description, self.source
-            )) < _ComparableTuple((
-                other.type, other.id, other.name, other.description, other.source
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((
-            self.type, self.id, self.name, self.description, self.source, tuple(self.references)
-        ))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<IssueType type={self.type}, id={self.id}, name={self.name}>'
diff --git a/cyclonedx/model/license.py b/cyclonedx/model/license.py
index 7c6a40e0..b4348993 100644
--- a/cyclonedx/model/license.py
+++ b/cyclonedx/model/license.py
@@ -26,7 +26,7 @@
 from warnings import warn
 from xml.etree.ElementTree import Element  # nosec B405
 
-import serializable
+import py_serializable as serializable
 from sortedcontainers import SortedSet
 
 from .._internal.compare import ComparableTuple as _ComparableTuple
@@ -218,24 +218,28 @@ def acknowledgement(self) -> Optional[LicenseAcknowledgement]:
     def acknowledgement(self, acknowledgement: Optional[LicenseAcknowledgement]) -> None:
         self._acknowledgement = acknowledgement
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self._acknowledgement,
+            self._id, self._name,
+            self._url,
+            self._text,
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, DisjunctiveLicense):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, DisjunctiveLicense):
-            return _ComparableTuple((
-                self._id, self._name
-            )) < _ComparableTuple((
-                other._id, other._name
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         if isinstance(other, LicenseExpression):
             return False  # self after any LicenseExpression
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self._id, self._name, self._text, self._url, self._acknowledgement))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<License id={self._id!r}, name={self._name!r}>'
@@ -311,17 +315,23 @@ def acknowledgement(self) -> Optional[LicenseAcknowledgement]:
     def acknowledgement(self, acknowledgement: Optional[LicenseAcknowledgement]) -> None:
         self._acknowledgement = acknowledgement
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self._acknowledgement,
+            self._value,
+        ))
+
     def __hash__(self) -> int:
-        return hash((self._value, self._acknowledgement))
+        return hash(self.__comparable_tuple())
 
     def __eq__(self, other: object) -> bool:
         if isinstance(other, LicenseExpression):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, LicenseExpression):
-            return self._value < other._value
+            return self.__comparable_tuple() < other.__comparable_tuple()
         if isinstance(other, DisjunctiveLicense):
             return True  # self before any DisjunctiveLicense
         return NotImplemented
diff --git a/cyclonedx/model/lifecycle.py b/cyclonedx/model/lifecycle.py
index 7975e339..db688bb8 100644
--- a/cyclonedx/model/lifecycle.py
+++ b/cyclonedx/model/lifecycle.py
@@ -30,15 +30,15 @@
 from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type, Union
 from xml.etree.ElementTree import Element  # nosec B405
 
-import serializable
-from serializable.helpers import BaseHelper
+import py_serializable as serializable
+from py_serializable.helpers import BaseHelper
 from sortedcontainers import SortedSet
 
 from .._internal.compare import ComparableTuple as _ComparableTuple
 from ..exception.serialization import CycloneDxDeserializationException
 
 if TYPE_CHECKING:  # pragma: no cover
-    from serializable import ViewType
+    from py_serializable import ViewType
 
 
 @serializable.serializable_enum
@@ -83,7 +83,7 @@ def __hash__(self) -> int:
 
     def __eq__(self, other: object) -> bool:
         if isinstance(other, PredefinedLifecycle):
-            return hash(other) == hash(self)
+            return self._phase == other._phase
         return False
 
     def __lt__(self, other: Any) -> bool:
@@ -142,19 +142,22 @@ def description(self) -> Optional[str]:
     def description(self, description: Optional[str]) -> None:
         self._description = description
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self._name, self._description
+        ))
+
     def __hash__(self) -> int:
-        return hash((self._name, self._description))
+        return hash(self.__comparable_tuple())
 
     def __eq__(self, other: object) -> bool:
         if isinstance(other, NamedLifecycle):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, NamedLifecycle):
-            return _ComparableTuple((self._name, self._description)) < _ComparableTuple(
-                (other._name, other._description)
-            )
+            return self.__comparable_tuple() < other.__comparable_tuple()
         if isinstance(other, PredefinedLifecycle):
             return False  # put NamedLifecycle after any PredefinedLifecycle
         return NotImplemented
diff --git a/cyclonedx/model/release_note.py b/cyclonedx/model/release_note.py
index c6591735..4509bb2b 100644
--- a/cyclonedx/model/release_note.py
+++ b/cyclonedx/model/release_note.py
@@ -18,9 +18,10 @@
 from datetime import datetime
 from typing import Iterable, Optional
 
-import serializable
+import py_serializable as serializable
 from sortedcontainers import SortedSet
 
+from .._internal.compare import ComparableTuple as _ComparableTuple
 from ..model import Note, Property, XsUri
 from ..model.issue import IssueType
 
@@ -233,16 +234,23 @@ def properties(self) -> 'SortedSet[Property]':
     def properties(self, properties: Iterable[Property]) -> None:
         self._properties = SortedSet(properties)
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.type, self.title, self.featured_image, self.social_image, self.description, self.timestamp,
+            _ComparableTuple(self.aliases),
+            _ComparableTuple(self.tags),
+            _ComparableTuple(self.resolves),
+            _ComparableTuple(self.notes),
+            _ComparableTuple(self.properties)
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, ReleaseNotes):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __hash__(self) -> int:
-        return hash((
-            self.type, self.title, self.featured_image, self.social_image, self.description, self.timestamp,
-            tuple(self.aliases), tuple(self.tags), tuple(self.resolves), tuple(self.notes), tuple(self.properties)
-        ))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<ReleaseNotes type={self.type}, title={self.title}>'
diff --git a/cyclonedx/model/service.py b/cyclonedx/model/service.py
index f96ef1da..91541f6b 100644
--- a/cyclonedx/model/service.py
+++ b/cyclonedx/model/service.py
@@ -26,7 +26,7 @@
 
 from typing import Any, Iterable, Optional, Union
 
-import serializable
+import py_serializable as serializable
 from sortedcontainers import SortedSet
 
 from .._internal.bom_ref import bom_ref_from_str as _bom_ref_from_str
@@ -352,26 +352,29 @@ def release_notes(self) -> Optional[ReleaseNotes]:
     def release_notes(self, release_notes: Optional[ReleaseNotes]) -> None:
         self._release_notes = release_notes
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.group, self.name, self.version,
+            self.bom_ref.value,
+            self.provider, self.description,
+            self.authenticated, _ComparableTuple(self.data), _ComparableTuple(self.endpoints),
+            _ComparableTuple(self.external_references), _ComparableTuple(self.licenses),
+            _ComparableTuple(self.properties), self.release_notes, _ComparableTuple(self.services),
+            self.x_trust_boundary
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, Service):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, Service):
-            return _ComparableTuple((
-                self.group, self.name, self.version
-            )) < _ComparableTuple((
-                other.group, other.name, other.version
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((
-            self.authenticated, tuple(self.data), self.description, tuple(self.endpoints),
-            tuple(self.external_references), self.group, tuple(self.licenses), self.name, tuple(self.properties),
-            self.provider, self.release_notes, tuple(self.services), self.version, self.x_trust_boundary
-        ))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<Service bom-ref={self.bom_ref}, group={self.group}, name={self.name}, version={self.version}>'
diff --git a/cyclonedx/model/tool.py b/cyclonedx/model/tool.py
index ff923bea..38b1e065 100644
--- a/cyclonedx/model/tool.py
+++ b/cyclonedx/model/tool.py
@@ -21,8 +21,8 @@
 from warnings import warn
 from xml.etree.ElementTree import Element  # nosec B405
 
-import serializable
-from serializable.helpers import BaseHelper
+import py_serializable as serializable
+from py_serializable.helpers import BaseHelper
 from sortedcontainers import SortedSet
 
 from .._internal.compare import ComparableTuple as _ComparableTuple
@@ -33,7 +33,7 @@
 from .service import Service
 
 if TYPE_CHECKING:  # pragma: no cover
-    from serializable import ObjectMetadataLibrary, ViewType
+    from py_serializable import ObjectMetadataLibrary, ViewType
 
 
 @serializable.serializable_class
@@ -148,22 +148,24 @@ def external_references(self) -> 'SortedSet[ExternalReference]':
     def external_references(self, external_references: Iterable[ExternalReference]) -> None:
         self._external_references = SortedSet(external_references)
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.vendor, self.name, self.version,
+            _ComparableTuple(self.hashes), _ComparableTuple(self.external_references)
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, Tool):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, Tool):
-            return _ComparableTuple((
-                self.vendor, self.name, self.version
-            )) < _ComparableTuple((
-                other.vendor, other.name, other.version
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.vendor, self.name, self.version, tuple(self.hashes), tuple(self.external_references)))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<Tool name={self.name}, version={self.version}, vendor={self.vendor}>'
@@ -250,16 +252,20 @@ def __bool__(self) -> bool:
             or len(self._components) > 0 \
             or len(self._services) > 0
 
-    def __eq__(self, other: object) -> bool:
-        if not isinstance(other, ToolRepository):
-            return False
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            _ComparableTuple(self._tools),
+            _ComparableTuple(self._components),
+            _ComparableTuple(self._services)
+        ))
 
-        return self._tools == other._tools \
-            and self._components == other._components \
-            and self._services == other._services
+    def __eq__(self, other: object) -> bool:
+        if isinstance(other, ToolRepository):
+            return self.__comparable_tuple() == other.__comparable_tuple()
+        return False
 
     def __hash__(self) -> int:
-        return hash((tuple(self._tools), tuple(self._components), tuple(self._services)))
+        return hash(self.__comparable_tuple())
 
 
 class _ToolRepositoryHelper(BaseHelper):
diff --git a/cyclonedx/model/vulnerability.py b/cyclonedx/model/vulnerability.py
index 2e110073..9ad3d52f 100644
--- a/cyclonedx/model/vulnerability.py
+++ b/cyclonedx/model/vulnerability.py
@@ -35,7 +35,7 @@
 from enum import Enum
 from typing import Any, Dict, FrozenSet, Iterable, Optional, Tuple, Type, Union
 
-import serializable
+import py_serializable as serializable
 from sortedcontainers import SortedSet
 
 from .._internal.bom_ref import bom_ref_from_str as _bom_ref_from_str
@@ -125,22 +125,23 @@ def status(self) -> Optional[ImpactAnalysisAffectedStatus]:
     def status(self, status: Optional[ImpactAnalysisAffectedStatus]) -> None:
         self._status = status
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.version, self.range, self.status
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, BomTargetVersionRange):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, BomTargetVersionRange):
-            return _ComparableTuple((
-                self.version, self.range, self.status
-            )) < _ComparableTuple((
-                other.version, other.range, other.status
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.version, self.range, self.status))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<BomTargetVersionRange version={self.version}, version_range={self.range}, status={self.status}>'
@@ -196,18 +197,24 @@ def versions(self) -> 'SortedSet[BomTargetVersionRange]':
     def versions(self, versions: Iterable[BomTargetVersionRange]) -> None:
         self._versions = SortedSet(versions)
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.ref,
+            _ComparableTuple(self.versions)
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, BomTarget):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, BomTarget):
-            return self.ref < other.ref
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.ref, tuple(self.versions)))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<BomTarget ref={self.ref}>'
@@ -320,13 +327,18 @@ def detail(self, detail: Optional[str]) -> None:
     # def last_updated(self, ...) -> None:
     #     ...  # TODO since CDX 1.5
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.state, self.justification, tuple(self.responses), self.detail
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, VulnerabilityAnalysis):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __hash__(self) -> int:
-        return hash((self.state, self.justification, tuple(self.responses), self.detail))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<VulnerabilityAnalysis state={self.state}, justification={self.justification}>'
@@ -374,22 +386,23 @@ def url(self) -> XsUri:
     def url(self, url: XsUri) -> None:
         self._url = url
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.title, self.url
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, VulnerabilityAdvisory):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, VulnerabilityAdvisory):
-            return _ComparableTuple((
-                self.title, self.url
-            )) < _ComparableTuple((
-                other.title, other.url
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.title, self.url))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<VulnerabilityAdvisory url={self.url}, title={self.title}>'
@@ -439,22 +452,23 @@ def url(self) -> Optional[XsUri]:
     def url(self, url: Optional[XsUri]) -> None:
         self._url = url
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.name, self.url
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, VulnerabilitySource):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, VulnerabilitySource):
-            return _ComparableTuple((
-                self.name, self.url
-            )) < _ComparableTuple((
-                other.name, other.url
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.name, self.url))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<VulnerabilityAdvisory name={self.name}, url={self.url}>'
@@ -472,61 +486,74 @@ class VulnerabilityReference:
 
     .. note::
         See the CycloneDX schema: https://cyclonedx.org/docs/1.6/xml/#type_vulnerabilityType
+
+    .. note::
+        Properties ``id`` and ``source`` are mandatory.
+
+        History:
+        * In v1.4 JSON scheme, both properties were mandatory
+          https://github.com/CycloneDX/specification/blob/d570ffb8956d796585b9574e57598c42ee9de770/schema/bom-1.4.schema.json#L1455-L1474
+        * In v1.4 XML schema, both properties were optional
+          https://github.com/CycloneDX/specification/blob/d570ffb8956d796585b9574e57598c42ee9de770/schema/bom-1.4.xsd#L1788-L1797
+        * In v1.5 XML schema, both were mandatory
+          https://github.com/CycloneDX/specification/blob/d570ffb8956d796585b9574e57598c42ee9de770/schema/bom-1.5.xsd#L3364-L3374
+
+        Decision:
+        Since CycloneDXCoreWorkingGroup chose JSON schema as the dominant schema, the one that serves as first spec
+        implementation, and since XML schema was "fixed" to work same as JSON schema, we'd consider it canon/spec that
+        both properties were always mandatory.
     """
 
     def __init__(
         self, *,
-        id: Optional[str] = None,
-        source: Optional[VulnerabilitySource] = None,
+        id: str,
+        source: VulnerabilitySource,
     ) -> None:
-        if not id and not source:
-            raise NoPropertiesProvidedException(
-                'Either id or source must be provided for a VulnerabilityReference - neither provided'
-            )
         self.id = id
         self.source = source
 
     @property
     @serializable.xml_sequence(1)
     @serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
-    def id(self) -> Optional[str]:
+    def id(self) -> str:
         """
         The identifier that uniquely identifies the vulnerability in the associated Source. For example: CVE-2021-39182.
         """
         return self._id
 
     @id.setter
-    def id(self, id: Optional[str]) -> None:
+    def id(self, id: str) -> None:
         self._id = id
 
     @property
     @serializable.xml_sequence(2)
-    def source(self) -> Optional[VulnerabilitySource]:
+    def source(self) -> VulnerabilitySource:
         """
         The source that published the vulnerability.
         """
         return self._source
 
     @source.setter
-    def source(self, source: Optional[VulnerabilitySource]) -> None:
+    def source(self, source: VulnerabilitySource) -> None:
         self._source = source
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.id, self.source
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, VulnerabilityReference):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, VulnerabilityReference):
-            return _ComparableTuple((
-                self.id, self.source
-            )) < _ComparableTuple((
-                other.id, other.source
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.id, self.source))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<VulnerabilityReference id={self.id}, source={self.source}>'
@@ -817,25 +844,30 @@ def justification(self) -> Optional[str]:
     def justification(self, justification: Optional[str]) -> None:
         self._justification = justification
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.severity, self.score or 0,
+            self.source, self.method, self.vector,
+            self.justification
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, VulnerabilityRating):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, VulnerabilityRating):
-            return _ComparableTuple((
-                self.severity, self.score, self.score or 0, self.method, self.vector, self.justification
-            )) < _ComparableTuple((
-                other.severity, other.score, other.score or 0, other.method, other.vector, other.justification
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((self.source, self.score or 0, self.severity, self.method, self.vector, self.justification))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
-        return f'<VulnerabilityRating score={self.score}, source={self.source}>'
+        return f'<VulnerabilityRating severity={self.severity} score={self.score}, ' \
+            f'source={self.source} method={self.method} vector={self.vector}' \
+            f'justification={self.justification}>'
 
 
 @serializable.serializable_class
@@ -890,18 +922,24 @@ def individuals(self) -> 'SortedSet[OrganizationalContact]':
     def individuals(self, individuals: Iterable[OrganizationalContact]) -> None:
         self._individuals = SortedSet(individuals)
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            _ComparableTuple(self.organizations),
+            _ComparableTuple(self.individuals)
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, VulnerabilityCredits):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, VulnerabilityCredits):
-            return hash(self) < hash(other)
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((tuple(self.organizations), tuple(self.individuals)))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<VulnerabilityCredits id={id(self)}>'
@@ -1289,26 +1327,30 @@ def properties(self) -> 'SortedSet[Property]':
     def properties(self, properties: Iterable[Property]) -> None:
         self._properties = SortedSet(properties)
 
+    def __comparable_tuple(self) -> _ComparableTuple:
+        return _ComparableTuple((
+            self.id, self.bom_ref.value,
+            self.source, _ComparableTuple(self.references),
+            _ComparableTuple(self.ratings), _ComparableTuple(self.cwes), self.description,
+            self.detail, self.recommendation, self.workaround, _ComparableTuple(self.advisories),
+            self.created, self.published, self.updated,
+            self.credits, self.tools, self.analysis,
+            _ComparableTuple(self.affects),
+            _ComparableTuple(self.properties)
+        ))
+
     def __eq__(self, other: object) -> bool:
         if isinstance(other, Vulnerability):
-            return hash(other) == hash(self)
+            return self.__comparable_tuple() == other.__comparable_tuple()
         return False
 
     def __lt__(self, other: Any) -> bool:
         if isinstance(other, Vulnerability):
-            return _ComparableTuple((
-                self.id, self.description, self.detail, self.source, self.created, self.published
-            )) < _ComparableTuple((
-                other.id, other.description, other.detail, other.source, other.created, other.published
-            ))
+            return self.__comparable_tuple() < other.__comparable_tuple()
         return NotImplemented
 
     def __hash__(self) -> int:
-        return hash((
-            self.id, self.source, tuple(self.references), tuple(self.ratings), tuple(self.cwes), self.description,
-            self.detail, self.recommendation, self.workaround, tuple(self.advisories), self.created, self.published,
-            self.updated, self.credits, self.tools, self.analysis, tuple(self.affects), tuple(self.properties)
-        ))
+        return hash(self.__comparable_tuple())
 
     def __repr__(self) -> str:
         return f'<Vulnerability bom-ref={self.bom_ref.value}, id={self.id}>'
diff --git a/cyclonedx/schema/schema.py b/cyclonedx/schema/schema.py
index 408e8a6a..a51686e9 100644
--- a/cyclonedx/schema/schema.py
+++ b/cyclonedx/schema/schema.py
@@ -18,7 +18,7 @@
 from abc import ABC, abstractmethod
 from typing import Dict, Literal, Type
 
-from serializable import ViewType
+from py_serializable import ViewType
 
 from . import SchemaVersion
 
diff --git a/cyclonedx/serialization/__init__.py b/cyclonedx/serialization/__init__.py
index 34489b28..aeab0364 100644
--- a/cyclonedx/serialization/__init__.py
+++ b/cyclonedx/serialization/__init__.py
@@ -25,7 +25,7 @@
 
 # See https://github.com/package-url/packageurl-python/issues/65
 from packageurl import PackageURL
-from serializable.helpers import BaseHelper
+from py_serializable.helpers import BaseHelper
 
 from ..exception.serialization import CycloneDxDeserializationException, SerializationOfUnexpectedValueException
 from ..model.bom_ref import BomRef
diff --git a/cyclonedx/spdx.py b/cyclonedx/spdx.py
index 8f7e30b1..9781af54 100644
--- a/cyclonedx/spdx.py
+++ b/cyclonedx/spdx.py
@@ -18,7 +18,7 @@
 
 __all__ = [
     'is_supported_id', 'fixup_id',
-    'is_compound_expression'
+    'is_expression'
 ]
 
 from json import load as json_load
@@ -47,26 +47,26 @@
 
 
 def is_supported_id(value: str) -> bool:
-    """Validate a SPDX-ID according to current spec."""
+    """Validate SPDX-ID according to current spec."""
     return value in __IDS
 
 
 def fixup_id(value: str) -> Optional[str]:
-    """Fixup a SPDX-ID.
+    """Fixup SPDX-ID.
 
     :returns: repaired value string, or `None` if fixup was unable to help.
     """
     return __IDS_LOWER_MAP.get(value.lower())
 
 
-def is_compound_expression(value: str) -> bool:
-    """Validate compound expression.
+def is_expression(value: str) -> bool:
+    """Validate SPDX license expression.
 
     .. note::
         Utilizes `license-expression library`_ to
         validate SPDX compound expression according to `SPDX license expression spec`_.
 
-    .. _SPDX license expression spec: https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions/
+    .. _SPDX license expression spec: https://spdx.github.io/spdx-spec/v3.0.1/annexes/spdx-license-expressions/
     .. _license-expression library: https://github.com/nexB/license-expression
     """
     try:
diff --git a/docs/conf.py b/docs/conf.py
index 86094a5d..e8c331f1 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -23,7 +23,7 @@
 
 # The full version, including alpha/beta/rc tags
 # !! version is managed by semantic_release
-release = '8.9.0'
+release = '9.0.1-rc.1'
 
 # -- General configuration ---------------------------------------------------
 
diff --git a/pyproject.toml b/pyproject.toml
index e8742131..2bcac94e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"
 [tool.poetry]
 name = "cyclonedx-python-lib"
 # !! version is managed by semantic_release
-version = "8.9.0"
+version = "9.0.1-rc.1"
 description = "Python library for CycloneDX"
 authors = [
   "Paul Horton <phorton@sonatype.com>",
@@ -70,7 +70,7 @@ keywords = [
 [tool.poetry.dependencies]
 python = "^3.8"
 packageurl-python = ">=0.11, <2"
-py-serializable =  "^1.1.1"
+py-serializable =  "^2.0.0"
 sortedcontainers = "^2.4.0"
 license-expression = "^30"
 jsonschema = { version = "^4.18", extras=['format'], optional=true }
diff --git a/tests/_data/models.py b/tests/_data/models.py
index 60a823c8..a312178a 100644
--- a/tests/_data/models.py
+++ b/tests/_data/models.py
@@ -1402,6 +1402,37 @@ def get_bom_with_definitions_and_detailed_standards() -> Bom:
         ]))
 
 
+def get_bom_for_issue540_duplicate_components() -> Bom:
+    # tests https://github.com/CycloneDX/cyclonedx-python-lib/issues/540
+    bom = _make_bom()
+    bom.metadata.component = root_component = Component(
+        name='myApp',
+        type=ComponentType.APPLICATION,
+        bom_ref='myApp'
+    )
+    component1 = Component(
+        type=ComponentType.LIBRARY,
+        name='some-component',
+        bom_ref='some-component'
+    )
+    bom.components.add(component1)
+    bom.register_dependency(root_component, [component1])
+    component2 = Component(
+        type=ComponentType.LIBRARY,
+        name='some-library',
+        bom_ref='some-library1'
+    )
+    bom.components.add(component2)
+    bom.register_dependency(component1, [component2])
+    component3 = Component(
+        type=ComponentType.LIBRARY,
+        name='some-library',
+        bom_ref='some-library2'
+    )
+    bom.components.add(component3)
+    bom.register_dependency(component1, [component3])
+    return bom
+
 # ---
 
 
diff --git a/tests/_data/own/json/1.5/issue677.json b/tests/_data/own/json/1.5/issue677.json
new file mode 100644
index 00000000..090d97f8
--- /dev/null
+++ b/tests/_data/own/json/1.5/issue677.json
@@ -0,0 +1,49 @@
+{
+  "bomFormat": "CycloneDX",
+  "specVersion": "1.5",
+  "serialNumber": "urn:uuid:66fa5692-2e9d-45c5-830a-ec8ccaf7dcc9",
+  "version": 1,
+  "metadata": {
+    "component": {
+      "description": "see issue #677 - https://github.com/CycloneDX/cyclonedx-python-lib/issues/677",
+      "type": "application",
+      "name": "test"
+    }
+  },
+  "components": [
+    {
+      "type": "operating-system",
+      "bom-ref": "test12",
+      "name": "alpine"
+    },
+    {
+      "type": "container",
+      "bom-ref": "test11",
+      "name": "alpine"
+    },
+    {
+      "type": "operating-system",
+      "bom-ref": "test22",
+      "name": "alpine"
+    },
+    {
+      "type": "container",
+      "bom-ref": "test21",
+      "name": "alpine"
+    }
+  ],
+  "dependencies": [
+    {
+      "ref": "test11",
+      "dependsOn": [
+        "test12"
+      ]
+    },
+    {
+      "ref": "test21",
+      "dependsOn": [
+        "test22"
+      ]
+    }
+  ]
+}
diff --git a/tests/_data/own/json/1.5/issue753.json b/tests/_data/own/json/1.5/issue753.json
new file mode 100644
index 00000000..15123220
--- /dev/null
+++ b/tests/_data/own/json/1.5/issue753.json
@@ -0,0 +1,37 @@
+{
+  "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
+  "bomFormat": "CycloneDX",
+  "specVersion": "1.5",
+  "version": 1,
+  "metadata": {
+    "component": {
+      "type": "application",
+      "name": "example",
+      "version": "1.2.3",
+      "bom-ref": "topref"
+    }
+  },
+  "components": [
+    {
+      "type": "library",
+      "name": "styled-engine",
+      "version": "5.16.6",
+      "bom-ref": "@mui/styled-engine@npm:5.16.6 [296f2]"
+    },
+    {
+      "type": "library",
+      "name": "styled-engine",
+      "version": "5.16.6",
+      "bom-ref": "@mui/styled-engine@npm:5.16.6 [3135b]"
+    }
+  ],
+  "dependencies": [
+    {
+      "ref": "topref",
+      "dependsOn": [
+        "@mui/styled-engine@npm:5.16.6 [296f2]",
+        "@mui/styled-engine@npm:5.16.6 [3135b]"
+      ]
+    }
+  ]
+}
diff --git a/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.0.xml.bin b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.0.xml.bin
new file mode 100644
index 00000000..dd1947eb
--- /dev/null
+++ b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.0.xml.bin
@@ -0,0 +1,20 @@
+<?xml version="1.0" ?>
+<bom xmlns="http://cyclonedx.org/schema/bom/1.0" version="1">
+  <components>
+    <component type="library">
+      <name>some-component</name>
+      <version/>
+      <modified>false</modified>
+    </component>
+    <component type="library">
+      <name>some-library</name>
+      <version/>
+      <modified>false</modified>
+    </component>
+    <component type="library">
+      <name>some-library</name>
+      <version/>
+      <modified>false</modified>
+    </component>
+  </components>
+</bom>
diff --git a/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.1.xml.bin b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.1.xml.bin
new file mode 100644
index 00000000..6d4d9bdb
--- /dev/null
+++ b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.1.xml.bin
@@ -0,0 +1,17 @@
+<?xml version="1.0" ?>
+<bom xmlns="http://cyclonedx.org/schema/bom/1.1" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
+  <components>
+    <component type="library" bom-ref="some-component">
+      <name>some-component</name>
+      <version/>
+    </component>
+    <component type="library" bom-ref="some-library1">
+      <name>some-library</name>
+      <version/>
+    </component>
+    <component type="library" bom-ref="some-library2">
+      <name>some-library</name>
+      <version/>
+    </component>
+  </components>
+</bom>
diff --git a/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.2.json.bin b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.2.json.bin
new file mode 100644
index 00000000..fd8f32b5
--- /dev/null
+++ b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.2.json.bin
@@ -0,0 +1,57 @@
+{
+  "components": [
+    {
+      "bom-ref": "some-component",
+      "name": "some-component",
+      "type": "library",
+      "version": ""
+    },
+    {
+      "bom-ref": "some-library1",
+      "name": "some-library",
+      "type": "library",
+      "version": ""
+    },
+    {
+      "bom-ref": "some-library2",
+      "name": "some-library",
+      "type": "library",
+      "version": ""
+    }
+  ],
+  "dependencies": [
+    {
+      "dependsOn": [
+        "some-component"
+      ],
+      "ref": "myApp"
+    },
+    {
+      "dependsOn": [
+        "some-library1",
+        "some-library2"
+      ],
+      "ref": "some-component"
+    },
+    {
+      "ref": "some-library1"
+    },
+    {
+      "ref": "some-library2"
+    }
+  ],
+  "metadata": {
+    "component": {
+      "bom-ref": "myApp",
+      "name": "myApp",
+      "type": "application",
+      "version": ""
+    },
+    "timestamp": "2023-01-07T13:44:32.312678+00:00"
+  },
+  "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
+  "version": 1,
+  "$schema": "http://cyclonedx.org/schema/bom-1.2b.schema.json",
+  "bomFormat": "CycloneDX",
+  "specVersion": "1.2"
+}
\ No newline at end of file
diff --git a/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.2.xml.bin b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.2.xml.bin
new file mode 100644
index 00000000..625bc1dc
--- /dev/null
+++ b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.2.xml.bin
@@ -0,0 +1,35 @@
+<?xml version="1.0" ?>
+<bom xmlns="http://cyclonedx.org/schema/bom/1.2" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
+  <metadata>
+    <timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
+    <component type="application" bom-ref="myApp">
+      <name>myApp</name>
+      <version/>
+    </component>
+  </metadata>
+  <components>
+    <component type="library" bom-ref="some-component">
+      <name>some-component</name>
+      <version/>
+    </component>
+    <component type="library" bom-ref="some-library1">
+      <name>some-library</name>
+      <version/>
+    </component>
+    <component type="library" bom-ref="some-library2">
+      <name>some-library</name>
+      <version/>
+    </component>
+  </components>
+  <dependencies>
+    <dependency ref="myApp">
+      <dependency ref="some-component"/>
+    </dependency>
+    <dependency ref="some-component">
+      <dependency ref="some-library1"/>
+      <dependency ref="some-library2"/>
+    </dependency>
+    <dependency ref="some-library1"/>
+    <dependency ref="some-library2"/>
+  </dependencies>
+</bom>
diff --git a/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.3.json.bin b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.3.json.bin
new file mode 100644
index 00000000..601c5974
--- /dev/null
+++ b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.3.json.bin
@@ -0,0 +1,57 @@
+{
+  "components": [
+    {
+      "bom-ref": "some-component",
+      "name": "some-component",
+      "type": "library",
+      "version": ""
+    },
+    {
+      "bom-ref": "some-library1",
+      "name": "some-library",
+      "type": "library",
+      "version": ""
+    },
+    {
+      "bom-ref": "some-library2",
+      "name": "some-library",
+      "type": "library",
+      "version": ""
+    }
+  ],
+  "dependencies": [
+    {
+      "dependsOn": [
+        "some-component"
+      ],
+      "ref": "myApp"
+    },
+    {
+      "dependsOn": [
+        "some-library1",
+        "some-library2"
+      ],
+      "ref": "some-component"
+    },
+    {
+      "ref": "some-library1"
+    },
+    {
+      "ref": "some-library2"
+    }
+  ],
+  "metadata": {
+    "component": {
+      "bom-ref": "myApp",
+      "name": "myApp",
+      "type": "application",
+      "version": ""
+    },
+    "timestamp": "2023-01-07T13:44:32.312678+00:00"
+  },
+  "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
+  "version": 1,
+  "$schema": "http://cyclonedx.org/schema/bom-1.3a.schema.json",
+  "bomFormat": "CycloneDX",
+  "specVersion": "1.3"
+}
\ No newline at end of file
diff --git a/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.3.xml.bin b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.3.xml.bin
new file mode 100644
index 00000000..9ca84692
--- /dev/null
+++ b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.3.xml.bin
@@ -0,0 +1,35 @@
+<?xml version="1.0" ?>
+<bom xmlns="http://cyclonedx.org/schema/bom/1.3" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
+  <metadata>
+    <timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
+    <component type="application" bom-ref="myApp">
+      <name>myApp</name>
+      <version/>
+    </component>
+  </metadata>
+  <components>
+    <component type="library" bom-ref="some-component">
+      <name>some-component</name>
+      <version/>
+    </component>
+    <component type="library" bom-ref="some-library1">
+      <name>some-library</name>
+      <version/>
+    </component>
+    <component type="library" bom-ref="some-library2">
+      <name>some-library</name>
+      <version/>
+    </component>
+  </components>
+  <dependencies>
+    <dependency ref="myApp">
+      <dependency ref="some-component"/>
+    </dependency>
+    <dependency ref="some-component">
+      <dependency ref="some-library1"/>
+      <dependency ref="some-library2"/>
+    </dependency>
+    <dependency ref="some-library1"/>
+    <dependency ref="some-library2"/>
+  </dependencies>
+</bom>
diff --git a/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.4.json.bin b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.4.json.bin
new file mode 100644
index 00000000..1da35538
--- /dev/null
+++ b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.4.json.bin
@@ -0,0 +1,53 @@
+{
+  "components": [
+    {
+      "bom-ref": "some-component",
+      "name": "some-component",
+      "type": "library"
+    },
+    {
+      "bom-ref": "some-library1",
+      "name": "some-library",
+      "type": "library"
+    },
+    {
+      "bom-ref": "some-library2",
+      "name": "some-library",
+      "type": "library"
+    }
+  ],
+  "dependencies": [
+    {
+      "dependsOn": [
+        "some-component"
+      ],
+      "ref": "myApp"
+    },
+    {
+      "dependsOn": [
+        "some-library1",
+        "some-library2"
+      ],
+      "ref": "some-component"
+    },
+    {
+      "ref": "some-library1"
+    },
+    {
+      "ref": "some-library2"
+    }
+  ],
+  "metadata": {
+    "component": {
+      "bom-ref": "myApp",
+      "name": "myApp",
+      "type": "application"
+    },
+    "timestamp": "2023-01-07T13:44:32.312678+00:00"
+  },
+  "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
+  "version": 1,
+  "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json",
+  "bomFormat": "CycloneDX",
+  "specVersion": "1.4"
+}
\ No newline at end of file
diff --git a/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.4.xml.bin b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.4.xml.bin
new file mode 100644
index 00000000..22db0779
--- /dev/null
+++ b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.4.xml.bin
@@ -0,0 +1,31 @@
+<?xml version="1.0" ?>
+<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
+  <metadata>
+    <timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
+    <component type="application" bom-ref="myApp">
+      <name>myApp</name>
+    </component>
+  </metadata>
+  <components>
+    <component type="library" bom-ref="some-component">
+      <name>some-component</name>
+    </component>
+    <component type="library" bom-ref="some-library1">
+      <name>some-library</name>
+    </component>
+    <component type="library" bom-ref="some-library2">
+      <name>some-library</name>
+    </component>
+  </components>
+  <dependencies>
+    <dependency ref="myApp">
+      <dependency ref="some-component"/>
+    </dependency>
+    <dependency ref="some-component">
+      <dependency ref="some-library1"/>
+      <dependency ref="some-library2"/>
+    </dependency>
+    <dependency ref="some-library1"/>
+    <dependency ref="some-library2"/>
+  </dependencies>
+</bom>
diff --git a/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.5.json.bin b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.5.json.bin
new file mode 100644
index 00000000..64c0371b
--- /dev/null
+++ b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.5.json.bin
@@ -0,0 +1,63 @@
+{
+  "components": [
+    {
+      "bom-ref": "some-component",
+      "name": "some-component",
+      "type": "library"
+    },
+    {
+      "bom-ref": "some-library1",
+      "name": "some-library",
+      "type": "library"
+    },
+    {
+      "bom-ref": "some-library2",
+      "name": "some-library",
+      "type": "library"
+    }
+  ],
+  "dependencies": [
+    {
+      "dependsOn": [
+        "some-component"
+      ],
+      "ref": "myApp"
+    },
+    {
+      "dependsOn": [
+        "some-library1",
+        "some-library2"
+      ],
+      "ref": "some-component"
+    },
+    {
+      "ref": "some-library1"
+    },
+    {
+      "ref": "some-library2"
+    }
+  ],
+  "metadata": {
+    "component": {
+      "bom-ref": "myApp",
+      "name": "myApp",
+      "type": "application"
+    },
+    "timestamp": "2023-01-07T13:44:32.312678+00:00"
+  },
+  "properties": [
+    {
+      "name": "key1",
+      "value": "val1"
+    },
+    {
+      "name": "key2",
+      "value": "val2"
+    }
+  ],
+  "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
+  "version": 1,
+  "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
+  "bomFormat": "CycloneDX",
+  "specVersion": "1.5"
+}
\ No newline at end of file
diff --git a/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.5.xml.bin b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.5.xml.bin
new file mode 100644
index 00000000..9146c15a
--- /dev/null
+++ b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.5.xml.bin
@@ -0,0 +1,35 @@
+<?xml version="1.0" ?>
+<bom xmlns="http://cyclonedx.org/schema/bom/1.5" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
+  <metadata>
+    <timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
+    <component type="application" bom-ref="myApp">
+      <name>myApp</name>
+    </component>
+  </metadata>
+  <components>
+    <component type="library" bom-ref="some-component">
+      <name>some-component</name>
+    </component>
+    <component type="library" bom-ref="some-library1">
+      <name>some-library</name>
+    </component>
+    <component type="library" bom-ref="some-library2">
+      <name>some-library</name>
+    </component>
+  </components>
+  <dependencies>
+    <dependency ref="myApp">
+      <dependency ref="some-component"/>
+    </dependency>
+    <dependency ref="some-component">
+      <dependency ref="some-library1"/>
+      <dependency ref="some-library2"/>
+    </dependency>
+    <dependency ref="some-library1"/>
+    <dependency ref="some-library2"/>
+  </dependencies>
+  <properties>
+    <property name="key1">val1</property>
+    <property name="key2">val2</property>
+  </properties>
+</bom>
diff --git a/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.6.json.bin b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.6.json.bin
new file mode 100644
index 00000000..0ce6b2c9
--- /dev/null
+++ b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.6.json.bin
@@ -0,0 +1,63 @@
+{
+  "components": [
+    {
+      "bom-ref": "some-component",
+      "name": "some-component",
+      "type": "library"
+    },
+    {
+      "bom-ref": "some-library1",
+      "name": "some-library",
+      "type": "library"
+    },
+    {
+      "bom-ref": "some-library2",
+      "name": "some-library",
+      "type": "library"
+    }
+  ],
+  "dependencies": [
+    {
+      "dependsOn": [
+        "some-component"
+      ],
+      "ref": "myApp"
+    },
+    {
+      "dependsOn": [
+        "some-library1",
+        "some-library2"
+      ],
+      "ref": "some-component"
+    },
+    {
+      "ref": "some-library1"
+    },
+    {
+      "ref": "some-library2"
+    }
+  ],
+  "metadata": {
+    "component": {
+      "bom-ref": "myApp",
+      "name": "myApp",
+      "type": "application"
+    },
+    "timestamp": "2023-01-07T13:44:32.312678+00:00"
+  },
+  "properties": [
+    {
+      "name": "key1",
+      "value": "val1"
+    },
+    {
+      "name": "key2",
+      "value": "val2"
+    }
+  ],
+  "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
+  "version": 1,
+  "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
+  "bomFormat": "CycloneDX",
+  "specVersion": "1.6"
+}
\ No newline at end of file
diff --git a/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.6.xml.bin b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.6.xml.bin
new file mode 100644
index 00000000..c64cf536
--- /dev/null
+++ b/tests/_data/snapshots/get_bom_for_issue540_duplicate_components-1.6.xml.bin
@@ -0,0 +1,35 @@
+<?xml version="1.0" ?>
+<bom xmlns="http://cyclonedx.org/schema/bom/1.6" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
+  <metadata>
+    <timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
+    <component type="application" bom-ref="myApp">
+      <name>myApp</name>
+    </component>
+  </metadata>
+  <components>
+    <component type="library" bom-ref="some-component">
+      <name>some-component</name>
+    </component>
+    <component type="library" bom-ref="some-library1">
+      <name>some-library</name>
+    </component>
+    <component type="library" bom-ref="some-library2">
+      <name>some-library</name>
+    </component>
+  </components>
+  <dependencies>
+    <dependency ref="myApp">
+      <dependency ref="some-component"/>
+    </dependency>
+    <dependency ref="some-component">
+      <dependency ref="some-library1"/>
+      <dependency ref="some-library2"/>
+    </dependency>
+    <dependency ref="some-library1"/>
+    <dependency ref="some-library2"/>
+  </dependencies>
+  <properties>
+    <property name="key1">val1</property>
+    <property name="key2">val2</property>
+  </properties>
+</bom>
diff --git a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.0.xml.bin b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.0.xml.bin
index e70bbd27..5262d1d2 100644
--- a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.0.xml.bin
+++ b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.0.xml.bin
@@ -4,13 +4,13 @@
     <component type="library">
       <name>dummy</name>
       <version>2.3.5</version>
-      <purl>pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6</purl>
+      <purl>pkg:pypi/pathlib2@2.3.5</purl>
       <modified>false</modified>
     </component>
     <component type="library">
       <name>dummy</name>
       <version>2.3.5</version>
-      <purl>pkg:pypi/pathlib2@2.3.5</purl>
+      <purl>pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6</purl>
       <modified>false</modified>
     </component>
   </components>
diff --git a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.1.xml.bin b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.1.xml.bin
index baf7bca9..7e9f29bb 100644
--- a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.1.xml.bin
+++ b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.1.xml.bin
@@ -1,15 +1,15 @@
 <?xml version="1.0" ?>
 <bom xmlns="http://cyclonedx.org/schema/bom/1.1" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
   <components>
-    <component type="library" bom-ref="dummy-b">
+    <component type="library" bom-ref="dummy-a">
       <name>dummy</name>
       <version>2.3.5</version>
-      <purl>pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6</purl>
+      <purl>pkg:pypi/pathlib2@2.3.5</purl>
     </component>
-    <component type="library" bom-ref="dummy-a">
+    <component type="library" bom-ref="dummy-b">
       <name>dummy</name>
       <version>2.3.5</version>
-      <purl>pkg:pypi/pathlib2@2.3.5</purl>
+      <purl>pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6</purl>
     </component>
   </components>
 </bom>
diff --git a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.2.json.bin b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.2.json.bin
index 651e8e36..cba8ccc0 100644
--- a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.2.json.bin
+++ b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.2.json.bin
@@ -1,16 +1,16 @@
 {
   "components": [
     {
-      "bom-ref": "dummy-b",
+      "bom-ref": "dummy-a",
       "name": "dummy",
-      "purl": "pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6",
+      "purl": "pkg:pypi/pathlib2@2.3.5",
       "type": "library",
       "version": "2.3.5"
     },
     {
-      "bom-ref": "dummy-a",
+      "bom-ref": "dummy-b",
       "name": "dummy",
-      "purl": "pkg:pypi/pathlib2@2.3.5",
+      "purl": "pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6",
       "type": "library",
       "version": "2.3.5"
     }
diff --git a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.2.xml.bin b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.2.xml.bin
index cf695a4d..8e52087f 100644
--- a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.2.xml.bin
+++ b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.2.xml.bin
@@ -4,15 +4,15 @@
     <timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
   </metadata>
   <components>
-    <component type="library" bom-ref="dummy-b">
+    <component type="library" bom-ref="dummy-a">
       <name>dummy</name>
       <version>2.3.5</version>
-      <purl>pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6</purl>
+      <purl>pkg:pypi/pathlib2@2.3.5</purl>
     </component>
-    <component type="library" bom-ref="dummy-a">
+    <component type="library" bom-ref="dummy-b">
       <name>dummy</name>
       <version>2.3.5</version>
-      <purl>pkg:pypi/pathlib2@2.3.5</purl>
+      <purl>pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6</purl>
     </component>
   </components>
   <dependencies>
diff --git a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.3.json.bin b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.3.json.bin
index 6ebec9dd..4f4e5528 100644
--- a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.3.json.bin
+++ b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.3.json.bin
@@ -1,16 +1,16 @@
 {
   "components": [
     {
-      "bom-ref": "dummy-b",
+      "bom-ref": "dummy-a",
       "name": "dummy",
-      "purl": "pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6",
+      "purl": "pkg:pypi/pathlib2@2.3.5",
       "type": "library",
       "version": "2.3.5"
     },
     {
-      "bom-ref": "dummy-a",
+      "bom-ref": "dummy-b",
       "name": "dummy",
-      "purl": "pkg:pypi/pathlib2@2.3.5",
+      "purl": "pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6",
       "type": "library",
       "version": "2.3.5"
     }
diff --git a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.3.xml.bin b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.3.xml.bin
index 9b5b5f7a..56808073 100644
--- a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.3.xml.bin
+++ b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.3.xml.bin
@@ -4,15 +4,15 @@
     <timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
   </metadata>
   <components>
-    <component type="library" bom-ref="dummy-b">
+    <component type="library" bom-ref="dummy-a">
       <name>dummy</name>
       <version>2.3.5</version>
-      <purl>pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6</purl>
+      <purl>pkg:pypi/pathlib2@2.3.5</purl>
     </component>
-    <component type="library" bom-ref="dummy-a">
+    <component type="library" bom-ref="dummy-b">
       <name>dummy</name>
       <version>2.3.5</version>
-      <purl>pkg:pypi/pathlib2@2.3.5</purl>
+      <purl>pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6</purl>
     </component>
   </components>
   <dependencies>
diff --git a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.4.json.bin b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.4.json.bin
index f1eeb9dc..5745f1cf 100644
--- a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.4.json.bin
+++ b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.4.json.bin
@@ -1,16 +1,16 @@
 {
   "components": [
     {
-      "bom-ref": "dummy-b",
+      "bom-ref": "dummy-a",
       "name": "dummy",
-      "purl": "pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6",
+      "purl": "pkg:pypi/pathlib2@2.3.5",
       "type": "library",
       "version": "2.3.5"
     },
     {
-      "bom-ref": "dummy-a",
+      "bom-ref": "dummy-b",
       "name": "dummy",
-      "purl": "pkg:pypi/pathlib2@2.3.5",
+      "purl": "pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6",
       "type": "library",
       "version": "2.3.5"
     }
diff --git a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.4.xml.bin b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.4.xml.bin
index cb9ea370..81a3cb22 100644
--- a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.4.xml.bin
+++ b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.4.xml.bin
@@ -4,15 +4,15 @@
     <timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
   </metadata>
   <components>
-    <component type="library" bom-ref="dummy-b">
+    <component type="library" bom-ref="dummy-a">
       <name>dummy</name>
       <version>2.3.5</version>
-      <purl>pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6</purl>
+      <purl>pkg:pypi/pathlib2@2.3.5</purl>
     </component>
-    <component type="library" bom-ref="dummy-a">
+    <component type="library" bom-ref="dummy-b">
       <name>dummy</name>
       <version>2.3.5</version>
-      <purl>pkg:pypi/pathlib2@2.3.5</purl>
+      <purl>pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6</purl>
     </component>
   </components>
   <dependencies>
diff --git a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.5.json.bin b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.5.json.bin
index 206aaec4..a88cf7ae 100644
--- a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.5.json.bin
+++ b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.5.json.bin
@@ -1,16 +1,16 @@
 {
   "components": [
     {
-      "bom-ref": "dummy-b",
+      "bom-ref": "dummy-a",
       "name": "dummy",
-      "purl": "pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6",
+      "purl": "pkg:pypi/pathlib2@2.3.5",
       "type": "library",
       "version": "2.3.5"
     },
     {
-      "bom-ref": "dummy-a",
+      "bom-ref": "dummy-b",
       "name": "dummy",
-      "purl": "pkg:pypi/pathlib2@2.3.5",
+      "purl": "pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6",
       "type": "library",
       "version": "2.3.5"
     }
diff --git a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.5.xml.bin b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.5.xml.bin
index 2944adfc..36630746 100644
--- a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.5.xml.bin
+++ b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.5.xml.bin
@@ -4,15 +4,15 @@
     <timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
   </metadata>
   <components>
-    <component type="library" bom-ref="dummy-b">
+    <component type="library" bom-ref="dummy-a">
       <name>dummy</name>
       <version>2.3.5</version>
-      <purl>pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6</purl>
+      <purl>pkg:pypi/pathlib2@2.3.5</purl>
     </component>
-    <component type="library" bom-ref="dummy-a">
+    <component type="library" bom-ref="dummy-b">
       <name>dummy</name>
       <version>2.3.5</version>
-      <purl>pkg:pypi/pathlib2@2.3.5</purl>
+      <purl>pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6</purl>
     </component>
   </components>
   <dependencies>
diff --git a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.6.json.bin b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.6.json.bin
index 77097c87..1251020b 100644
--- a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.6.json.bin
+++ b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.6.json.bin
@@ -1,16 +1,16 @@
 {
   "components": [
     {
-      "bom-ref": "dummy-b",
+      "bom-ref": "dummy-a",
       "name": "dummy",
-      "purl": "pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6",
+      "purl": "pkg:pypi/pathlib2@2.3.5",
       "type": "library",
       "version": "2.3.5"
     },
     {
-      "bom-ref": "dummy-a",
+      "bom-ref": "dummy-b",
       "name": "dummy",
-      "purl": "pkg:pypi/pathlib2@2.3.5",
+      "purl": "pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6",
       "type": "library",
       "version": "2.3.5"
     }
diff --git a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.6.xml.bin b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.6.xml.bin
index 92263f13..18390367 100644
--- a/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.6.xml.bin
+++ b/tests/_data/snapshots/get_bom_for_issue_598_multiple_components_with_purl_qualifiers-1.6.xml.bin
@@ -4,15 +4,15 @@
     <timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
   </metadata>
   <components>
-    <component type="library" bom-ref="dummy-b">
+    <component type="library" bom-ref="dummy-a">
       <name>dummy</name>
       <version>2.3.5</version>
-      <purl>pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6</purl>
+      <purl>pkg:pypi/pathlib2@2.3.5</purl>
     </component>
-    <component type="library" bom-ref="dummy-a">
+    <component type="library" bom-ref="dummy-b">
       <name>dummy</name>
       <version>2.3.5</version>
-      <purl>pkg:pypi/pathlib2@2.3.5</purl>
+      <purl>pkg:pypi/pathlib2@2.3.5?vcs_url=git%2Bhttps://github.com/jazzband/pathlib2.git%405a6a88db3cc1d08dbc86fbe15edfb69fb5f5a3d6</purl>
     </component>
   </components>
   <dependencies>
diff --git a/tests/test_factory_license.py b/tests/test_factory_license.py
index f7fd7b99..051a88b3 100644
--- a/tests/test_factory_license.py
+++ b/tests/test_factory_license.py
@@ -33,7 +33,7 @@ def test_make_from_string_with_id(self) -> None:
         expected = DisjunctiveLicense(id='bar', text=text, url=url, acknowledgement=acknowledgement)
 
         with unittest.mock.patch('cyclonedx.factory.license.spdx_fixup', return_value='bar'), \
-                unittest.mock.patch('cyclonedx.factory.license.is_spdx_compound_expression', return_value=True):
+                unittest.mock.patch('cyclonedx.factory.license.is_spdx_expression', return_value=True):
             actual = LicenseFactory().make_from_string('foo',
                                                        license_text=text,
                                                        license_url=url,
@@ -48,7 +48,7 @@ def test_make_from_string_with_name(self) -> None:
         expected = DisjunctiveLicense(name='foo', text=text, url=url, acknowledgement=acknowledgement)
 
         with unittest.mock.patch('cyclonedx.factory.license.spdx_fixup', return_value=None), \
-                unittest.mock.patch('cyclonedx.factory.license.is_spdx_compound_expression', return_value=False):
+                unittest.mock.patch('cyclonedx.factory.license.is_spdx_expression', return_value=False):
             actual = LicenseFactory().make_from_string('foo',
                                                        license_text=text,
                                                        license_url=url,
@@ -61,7 +61,7 @@ def test_make_from_string_with_expression(self) -> None:
         expected = LicenseExpression('foo', acknowledgement=acknowledgement)
 
         with unittest.mock.patch('cyclonedx.factory.license.spdx_fixup', return_value=None), \
-                unittest.mock.patch('cyclonedx.factory.license.is_spdx_compound_expression', return_value=True):
+                unittest.mock.patch('cyclonedx.factory.license.is_spdx_expression', return_value=True):
             actual = LicenseFactory().make_from_string('foo',
                                                        license_acknowledgement=acknowledgement)
 
@@ -94,11 +94,11 @@ def test_make_with_name(self) -> None:
     def test_make_with_expression(self) -> None:
         acknowledgement = unittest.mock.NonCallableMock(spec=LicenseAcknowledgement)
         expected = LicenseExpression('foo', acknowledgement=acknowledgement)
-        with unittest.mock.patch('cyclonedx.factory.license.is_spdx_compound_expression', return_value=True):
+        with unittest.mock.patch('cyclonedx.factory.license.is_spdx_expression', return_value=True):
             actual = LicenseFactory().make_with_expression(expression='foo', acknowledgement=acknowledgement)
         self.assertEqual(expected, actual)
 
     def test_make_with_expression_raises(self) -> None:
         with self.assertRaises(InvalidLicenseExpressionException, msg='foo'):
-            with unittest.mock.patch('cyclonedx.factory.license.is_spdx_compound_expression', return_value=False):
+            with unittest.mock.patch('cyclonedx.factory.license.is_spdx_expression', return_value=False):
                 LicenseFactory().make_with_expression('foo')
diff --git a/tests/test_model_component.py b/tests/test_model_component.py
index 54d3cfe6..3f893907 100644
--- a/tests/test_model_component.py
+++ b/tests/test_model_component.py
@@ -142,17 +142,17 @@ def test_multiple_basic_components(self) -> None:
         self.assertNotEqual(c1, c2)
 
     def test_external_references(self) -> None:
-        c = Component(name='test-component')
-        c.external_references.add(ExternalReference(
+        c1 = Component(name='test-component')
+        c1.external_references.add(ExternalReference(
             type=ExternalReferenceType.OTHER,
             url=XsUri('https://cyclonedx.org'),
             comment='No comment'
         ))
-        self.assertEqual(c.name, 'test-component')
-        self.assertIsNone(c.version)
-        self.assertEqual(c.type, ComponentType.LIBRARY)
-        self.assertEqual(len(c.external_references), 1)
-        self.assertEqual(len(c.hashes), 0)
+        self.assertEqual(c1.name, 'test-component')
+        self.assertIsNone(c1.version)
+        self.assertEqual(c1.type, ComponentType.LIBRARY)
+        self.assertEqual(len(c1.external_references), 1)
+        self.assertEqual(len(c1.hashes), 0)
 
         c2 = Component(name='test2-component')
         self.assertEqual(c2.name, 'test2-component')
@@ -170,39 +170,35 @@ def test_empty_component_with_version(self) -> None:
         self.assertEqual(len(c.hashes), 0)
 
     def test_component_equal_1(self) -> None:
-        c = Component(name='test-component')
-        c.external_references.add(ExternalReference(
+        c1 = Component(name='test-component')
+        c1.external_references.add(ExternalReference(
             type=ExternalReferenceType.OTHER,
             url=XsUri('https://cyclonedx.org'),
             comment='No comment'
         ))
-
         c2 = Component(name='test-component')
         c2.external_references.add(ExternalReference(
             type=ExternalReferenceType.OTHER,
             url=XsUri('https://cyclonedx.org'),
             comment='No comment'
         ))
-
-        self.assertEqual(c, c2)
+        self.assertEqual(c1, c2)
 
     def test_component_equal_2(self) -> None:
-        props: List[Property] = [
+        props: List[Property] = (
             Property(name='prop1', value='val1'),
-            Property(name='prop2', value='val2')
-        ]
-
-        c = Component(
+            Property(name='prop2', value='val2'),
+        )
+        c1 = Component(
             name='test-component', version='1.2.3', properties=props
         )
         c2 = Component(
             name='test-component', version='1.2.3', properties=props
         )
-
-        self.assertEqual(c, c2)
+        self.assertEqual(c1, c2)
 
     def test_component_equal_3(self) -> None:
-        c = Component(
+        c1 = Component(
             name='test-component', version='1.2.3', properties=[
                 Property(name='prop1', value='val1'),
                 Property(name='prop2', value='val2')
@@ -214,8 +210,16 @@ def test_component_equal_3(self) -> None:
                 Property(name='prop4', value='val4')
             ]
         )
+        self.assertNotEqual(c1, c2)
 
-        self.assertNotEqual(c, c2)
+    def test_component_equal_4(self) -> None:
+        c1 = Component(
+            name='test-component', version='1.2.3', bom_ref='ref1'
+        )
+        c2 = Component(
+            name='test-component', version='1.2.3', bom_ref='ref2'
+        )
+        self.assertNotEqual(c1, c2)
 
     def test_same_1(self) -> None:
         c1 = get_component_setuptools_simple()
@@ -347,8 +351,8 @@ def test_sort(self) -> None:
 class TestModelAttachedText(TestCase):
 
     def test_sort(self) -> None:
-        # expected sort order: (content_type, content, encoding)
-        expected_order = [0, 4, 2, 1, 3]
+        # expected sort order: (content_type, encoding, content)
+        expected_order = [0, 2, 4, 1, 3]
         text = [
             AttachedText(content='a', content_type='a', encoding=Encoding.BASE_64),
             AttachedText(content='a', content_type='b', encoding=Encoding.BASE_64),
@@ -438,17 +442,17 @@ def test_no_params(self) -> None:
     def test_same_1(self) -> None:
         p1 = get_pedigree_1()
         p2 = get_pedigree_1()
-        self.assertNotEqual(id(p1), id(p2))
-        self.assertEqual(hash(p1), hash(p2))
-        self.assertTrue(p1 == p2)
+        self.assertNotEqual(id(p1), id(p2), 'id')
+        self.assertEqual(hash(p1), hash(p2), 'hash')
+        self.assertTrue(p1 == p2, 'equal')
 
     def test_not_same_1(self) -> None:
         p1 = get_pedigree_1()
         p2 = get_pedigree_1()
         p2.notes = 'Some other notes here'
-        self.assertNotEqual(id(p1), id(p2))
-        self.assertNotEqual(hash(p1), hash(p2))
-        self.assertFalse(p1 == p2)
+        self.assertNotEqual(id(p1), id(p2), 'id')
+        self.assertNotEqual(hash(p1), hash(p2), 'hash')
+        self.assertFalse(p1 == p2, 'equal')
 
 
 class TestModelSwid(TestCase):
@@ -456,20 +460,20 @@ class TestModelSwid(TestCase):
     def test_same_1(self) -> None:
         sw_1 = get_swid_1()
         sw_2 = get_swid_1()
-        self.assertNotEqual(id(sw_1), id(sw_2))
-        self.assertEqual(hash(sw_1), hash(sw_2))
-        self.assertTrue(sw_1 == sw_2)
+        self.assertNotEqual(id(sw_1), id(sw_2), 'id')
+        self.assertEqual(hash(sw_1), hash(sw_2), 'hash')
+        self.assertTrue(sw_1 == sw_2, 'equal')
 
     def test_same_2(self) -> None:
         sw_1 = get_swid_2()
         sw_2 = get_swid_2()
-        self.assertNotEqual(id(sw_1), id(sw_2))
-        self.assertEqual(hash(sw_1), hash(sw_2))
-        self.assertTrue(sw_1 == sw_2)
+        self.assertNotEqual(id(sw_1), id(sw_2), 'id')
+        self.assertEqual(hash(sw_1), hash(sw_2), 'hash')
+        self.assertTrue(sw_1 == sw_2, 'equal')
 
     def test_not_same(self) -> None:
         sw_1 = get_swid_1()
         sw_2 = get_swid_2()
-        self.assertNotEqual(id(sw_1), id(sw_2))
-        self.assertNotEqual(hash(sw_1), hash(sw_2))
-        self.assertFalse(sw_1 == sw_2)
+        self.assertNotEqual(id(sw_1), id(sw_2), 'id')
+        self.assertNotEqual(hash(sw_1), hash(sw_2), 'hash')
+        self.assertFalse(sw_1 == sw_2, 'equal')
diff --git a/tests/test_model_vulnerability.py b/tests/test_model_vulnerability.py
index e18f6003..830cbba2 100644
--- a/tests/test_model_vulnerability.py
+++ b/tests/test_model_vulnerability.py
@@ -188,7 +188,7 @@ def test_sort(self) -> None:
         datetime2 = datetime1 + timedelta(seconds=5)
 
         # expected sort order: (id, description, detail, source, created, published)
-        expected_order = [0, 6, 1, 7, 2, 8, 3, 9, 4, 10, 5, 11]
+        expected_order = [0, 1, 10, 2, 3, 4, 5, 6, 7, 8, 9, 11]
         vulnerabilities = [
             Vulnerability(bom_ref='0', id='a', description='a', detail='a',
                           source=source1, created=datetime1, published=datetime1),
@@ -258,16 +258,12 @@ def test_sort(self) -> None:
         source_b = VulnerabilitySource(name='b')
 
         # expected sort order: ([id], [source])
-        expected_order = [0, 1, 4, 2, 3, 5, 6, 7]
+        expected_order = [2, 3, 1, 0]
         refs = [
+            VulnerabilityReference(id='b', source=source_b),
+            VulnerabilityReference(id='b', source=source_a),
             VulnerabilityReference(id='a', source=source_a),
             VulnerabilityReference(id='a', source=source_b),
-            VulnerabilityReference(id='b', source=source_a),
-            VulnerabilityReference(id='b', source=source_b),
-            VulnerabilityReference(id='a'),
-            VulnerabilityReference(id='b'),
-            VulnerabilityReference(source=source_a),
-            VulnerabilityReference(source=source_b),
         ]
         sorted_refs = sorted(refs)
         expected_refs = reorder(refs, expected_order)
@@ -281,7 +277,7 @@ def test_sort(self) -> None:
         method_a = VulnerabilityScoreSource.CVSS_V3_1
 
         # expected sort order: ([severity], [score], [source], [method], [vector], [justification])
-        expected_order = [0, 1, 2, 3, 4, 5, 6, 7]
+        expected_order = [5, 0, 1, 2, 3, 4, 6, 7]
         refs = [
             VulnerabilityRating(severity=VulnerabilitySeverity.HIGH, score=Decimal(10),
                                 source=source_a, method=method_a, vector='a', justification='a'),
@@ -298,6 +294,7 @@ def test_sort(self) -> None:
         ]
         sorted_refs = sorted(refs)
         expected_refs = reorder(refs, expected_order)
+        self.maxDiff = None  # gimme all diff on error
         self.assertListEqual(sorted_refs, expected_refs)
 
 
diff --git a/tests/test_real_world_examples.py b/tests/test_real_world_examples.py
index 1df29fca..0fe3e480 100644
--- a/tests/test_real_world_examples.py
+++ b/tests/test_real_world_examples.py
@@ -17,6 +17,7 @@
 
 import unittest
 from datetime import datetime
+from json import loads as json_loads
 from os.path import join
 from typing import Any
 from unittest.mock import patch
@@ -36,3 +37,19 @@ def test_webgoat_6_1(self, *_: Any, **__: Any) -> None:
     def test_regression_issue_630(self, *_: Any, **__: Any) -> None:
         with open(join(OWN_DATA_DIRECTORY, 'xml', '1.6', 'regression_issue630.xml')) as input_xml:
             Bom.from_xml(input_xml)
+
+    def test_regression_issue677(self, *_: Any, **__: Any) -> None:
+        # tests https://github.com/CycloneDX/cyclonedx-python-lib/issues/677
+        with open(join(OWN_DATA_DIRECTORY, 'json', '1.5', 'issue677.json')) as input_json:
+            json = json_loads(input_json.read())
+        bom = Bom.from_json(json)
+        self.assertEqual(4, len(bom.components))
+        bom.validate()
+
+    def test_regression_issue753(self, *_: Any, **__: Any) -> None:
+        # tests https://github.com/CycloneDX/cyclonedx-python-lib/issues/753
+        with open(join(OWN_DATA_DIRECTORY, 'json', '1.5', 'issue753.json')) as input_json:
+            json = json_loads(input_json.read())
+        bom = Bom.from_json(json)
+        self.assertEqual(2, len(bom.components))
+        bom.validate()
diff --git a/tests/test_spdx.py b/tests/test_spdx.py
index a174e5c0..f18c23d7 100644
--- a/tests/test_spdx.py
+++ b/tests/test_spdx.py
@@ -19,19 +19,62 @@
 from json import load as json_load
 from unittest import TestCase
 
-from ddt import data, ddt, idata, unpack
+from ddt import ddt, idata, unpack
 
 from cyclonedx import spdx
 from cyclonedx.schema._res import SPDX_JSON
 
 # rework access
 with open(SPDX_JSON) as spdx_schema:
-    KNOWN_SPDX_IDS = json_load(spdx_schema)['enum']
+    KNOWN_SPDX_IDS = set(json_load(spdx_schema)['enum'])
+
+# for valid test data see the spec: https://spdx.github.io/spdx-spec/v3.0.1/annexes/spdx-license-expressions/
+VALID_EXPRESSIONS = {
+    # region Simple license expressions
+    'CDDL-1.0',
+    # region not supported yet #110 - https://github.com/aboutcode-org/license-expression/issues/110
+    # 'CDDL-1.0+',
+    # endregion region not supported yet #110
+    # region not supported yet #109 - https://github.com/aboutcode-org/license-expression/issues/109
+    # 'LicenseRef-23',
+    # 'LicenseRef-MIT-Style-1',
+    # 'DocumentRef-spdx-tool-1.2:LicenseRef-MIT-Style-2',
+    # endregion region not supported yet #109
+    # endregion Simple license expressions
+    # region Composite license expressions
+    'LGPL-2.1-only OR MIT',
+    'MIT or LGPL-2.1-only',
+    '(MIT OR LGPL-2.1-only)',
+    'LGPL-2.1-only OR MIT OR BSD-3-Clause',
+    'LGPL-2.1-only AND MIT',
+    'MIT AND LGPL-2.1-only',
+    'MIT and LGPL-2.1-only',
+    '(MIT AND LGPL-2.1-only)',
+    'LGPL-2.1-only AND MIT AND BSD-2-Clause',
+    'GPL-2.0-or-later WITH Bison-exception-2.2',
+    'LGPL-2.1-only OR BSD-3-Clause AND MIT',
+    'MIT AND (LGPL-2.1-or-later OR BSD-3-Clause)',
+    # endregion Composite license expressions
+    # region examples from CDX spec
+    'Apache-2.0 AND (MIT OR GPL-2.0-only)',
+    'GPL-3.0-only WITH Classpath-exception-2.0',
+    # endregion examples from CDX spec
+}
+
+INVALID_EXPRESSIONS = {
+    'MIT AND Apache-2.0 OR something-unknown'
+    'something invalid',
+    '(c) John Doe',
+    'Apache License, Version 2.0',
+}
 
-VALID_COMPOUND_EXPRESSIONS = {
-    # for valid test data see the spec: https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions/
-    '(MIT AND Apache-2.0)',
-    'BSD-2-Clause OR Apache-2.0',
+UNKNOWN_SPDX_IDS = {
+    '',
+    'something unsupported', 'something unfixable',
+    'Apache 2.0',
+    'LicenseRef-custom-identifier',
+    *(VALID_EXPRESSIONS - KNOWN_SPDX_IDS),
+    *INVALID_EXPRESSIONS,
 }
 
 
@@ -43,12 +86,11 @@ def test_positive(self, supported_value: str) -> None:
         actual = spdx.is_supported_id(supported_value)
         self.assertTrue(actual)
 
-    @data(
-        'something unsupported',
-        # somehow case-twisted values
-        'MiT',
-        'mit',
-    )
+    @idata(chain(UNKNOWN_SPDX_IDS, (
+        # region somehow case-twisted values
+        'MiT', 'mit',
+        # endregion somehow case-twisted values
+    )))
     def test_negative(self, unsupported_value: str) -> None:
         actual = spdx.is_supported_id(unsupported_value)
         self.assertFalse(actual)
@@ -60,37 +102,31 @@ class TestSpdxFixup(TestCase):
     @idata(chain(
         # original value
         ((v, v) for v in KNOWN_SPDX_IDS),
-        # somehow case-twisted values
+        # region somehow case-twisted values
         ((v.lower(), v) for v in KNOWN_SPDX_IDS),
         ((v.upper(), v) for v in KNOWN_SPDX_IDS)
+        # endregion somehow case-twisted values
     ))
     @unpack
     def test_positive(self, fixable: str, expected_fixed: str) -> None:
         actual = spdx.fixup_id(fixable)
         self.assertEqual(expected_fixed, actual)
 
-    @data(
-        'something unfixable',
-    )
+    @idata(UNKNOWN_SPDX_IDS)
     def test_negative(self, unfixable: str) -> None:
         actual = spdx.fixup_id(unfixable)
         self.assertIsNone(actual)
 
 
 @ddt
-class TestSpdxIsCompoundExpression(TestCase):
+class TestSpdxIsExpression(TestCase):
 
-    @idata(VALID_COMPOUND_EXPRESSIONS)
+    @idata(VALID_EXPRESSIONS)
     def test_positive(self, valid_expression: str) -> None:
-        actual = spdx.is_compound_expression(valid_expression)
+        actual = spdx.is_expression(valid_expression)
         self.assertTrue(actual)
 
-    @data(
-        'MIT AND Apache-2.0 OR something-unknown'
-        'something invalid',
-        '(c) John Doe',
-        'Apache License, Version 2.0'
-    )
+    @idata(INVALID_EXPRESSIONS)
     def test_negative(self, invalid_expression: str) -> None:
-        actual = spdx.is_compound_expression(invalid_expression)
+        actual = spdx.is_expression(invalid_expression)
         self.assertFalse(actual)