2121"""
2222
2323from enum import Enum
24- from typing import TYPE_CHECKING , Any , Optional , Union
24+ from json import loads as json_loads
25+ from typing import TYPE_CHECKING , Any , Dict , List , Optional , Type , Union
2526from warnings import warn
27+ from xml .etree .ElementTree import Element # nosec B405
2628
2729import serializable
2830from sortedcontainers import SortedSet
2931
3032from .._internal .compare import ComparableTuple as _ComparableTuple
3133from ..exception .model import MutuallyExclusivePropertiesException
34+ from ..exception .serialization import CycloneDxDeserializationException
3235from ..schema .schema import SchemaVersion1Dot6
3336from . import AttachedText , XsUri
3437
@@ -350,6 +353,7 @@ class LicenseRepository(SortedSet[License]):
350353 Denormalizers/deserializers will be thankful.
351354 The normalization/serialization process SHOULD take care of these facts and do what is needed.
352355 """
356+
353357else :
354358 class LicenseRepository (SortedSet ):
355359 """Collection of :class:`License`.
@@ -364,3 +368,86 @@ class LicenseRepository(SortedSet):
364368 Denormalizers/deserializers will be thankful.
365369 The normalization/serialization process SHOULD take care of these facts and do what is needed.
366370 """
371+
372+
373+ class _LicenseRepositorySerializationHelper (serializable .helpers .BaseHelper ):
374+ """ THIS CLASS IS NON-PUBLIC API """
375+
376+ @classmethod
377+ def json_normalize (cls , o : LicenseRepository , * ,
378+ view : Optional [Type [serializable .ViewType ]],
379+ ** __ : Any ) -> Any :
380+ if len (o ) == 0 :
381+ return None
382+ expression = next ((li for li in o if isinstance (li , LicenseExpression )), None )
383+ if expression :
384+ # mixed license expression and license? this is an invalid constellation according to schema!
385+ # see https://github.com/CycloneDX/specification/pull/205
386+ # but models need to allow it for backwards compatibility with JSON CDX < 1.5
387+ return [json_loads (expression .as_json (view_ = view ))] # type:ignore[attr-defined]
388+ return [
389+ {'license' : json_loads (
390+ li .as_json ( # type:ignore[attr-defined]
391+ view_ = view )
392+ )}
393+ for li in o
394+ if isinstance (li , DisjunctiveLicense )
395+ ]
396+
397+ @classmethod
398+ def json_denormalize (cls , o : List [Dict [str , Any ]],
399+ ** __ : Any ) -> LicenseRepository :
400+ repo = LicenseRepository ()
401+ for li in o :
402+ if 'license' in li :
403+ repo .add (DisjunctiveLicense .from_json ( # type:ignore[attr-defined]
404+ li ['license' ]))
405+ elif 'expression' in li :
406+ repo .add (LicenseExpression .from_json ( # type:ignore[attr-defined]
407+ li
408+ ))
409+ else :
410+ raise CycloneDxDeserializationException (f'unexpected: { li !r} ' )
411+ return repo
412+
413+ @classmethod
414+ def xml_normalize (cls , o : LicenseRepository , * ,
415+ element_name : str ,
416+ view : Optional [Type [serializable .ViewType ]],
417+ xmlns : Optional [str ],
418+ ** __ : Any ) -> Optional [Element ]:
419+ if len (o ) == 0 :
420+ return None
421+ elem = Element (element_name )
422+ expression = next ((li for li in o if isinstance (li , LicenseExpression )), None )
423+ if expression :
424+ # mixed license expression and license? this is an invalid constellation according to schema!
425+ # see https://github.com/CycloneDX/specification/pull/205
426+ # but models need to allow it for backwards compatibility with JSON CDX < 1.5
427+ elem .append (expression .as_xml ( # type:ignore[attr-defined]
428+ view_ = view , as_string = False , element_name = 'expression' , xmlns = xmlns ))
429+ else :
430+ elem .extend (
431+ li .as_xml ( # type:ignore[attr-defined]
432+ view_ = view , as_string = False , element_name = 'license' , xmlns = xmlns )
433+ for li in o
434+ if isinstance (li , DisjunctiveLicense )
435+ )
436+ return elem
437+
438+ @classmethod
439+ def xml_denormalize (cls , o : Element ,
440+ default_ns : Optional [str ],
441+ ** __ : Any ) -> LicenseRepository :
442+ repo = LicenseRepository ()
443+ for li in o :
444+ tag = li .tag if default_ns is None else li .tag .replace (f'{{{ default_ns } }}' , '' )
445+ if tag == 'license' :
446+ repo .add (DisjunctiveLicense .from_xml ( # type:ignore[attr-defined]
447+ li , default_ns ))
448+ elif tag == 'expression' :
449+ repo .add (LicenseExpression .from_xml ( # type:ignore[attr-defined]
450+ li , default_ns ))
451+ else :
452+ raise CycloneDxDeserializationException (f'unexpected: { li !r} ' )
453+ return repo
0 commit comments