1616# Copyright (c) OWASP Foundation. All Rights Reserved.
1717
1818
19- from collections .abc import Iterable , Generator
19+ from collections .abc import Iterable
2020from decimal import Decimal
2121from enum import Enum
22+ from json import loads as json_loads
2223from typing import Any , Optional , Union
24+ from warnings import warn
2325from xml .etree .ElementTree import Element as XmlElement
2426
2527# See https://github.com/package-url/packageurl-python/issues/65
2628import py_serializable as serializable
2729from sortedcontainers import SortedSet
2830
29- from ..exception .serialization import SerializationOfUnexpectedValueException
3031from .._internal .bom_ref import bom_ref_from_str as _bom_ref_from_str
3132from .._internal .compare import ComparableTuple as _ComparableTuple
3233from ..exception .model import InvalidConfidenceException , InvalidValueException
@@ -151,12 +152,12 @@ class _IdentityToolRepositorySerializationHelper(serializable.helpers.BaseHelper
151152 """ THIS CLASS IS NON-PUBLIC API """
152153
153154 @classmethod
154- def json_serialize (cls , o : Iterable ['BomRef' ]) -> tuple [str ]:
155- return tuple (i .value for i in o )
155+ def json_serialize (cls , o : Iterable ['BomRef' ]) -> tuple [str , ... ]:
156+ return tuple (t .value for t in o if t . value )
156157
157158 @classmethod
158- def json_deserialize (cls , o : Iterable [str ]) -> tuple [BomRef ]:
159- return tuple (BomRef (value = i ) for i in o )
159+ def json_deserialize (cls , o : Iterable [str ]) -> tuple [BomRef , ... ]:
160+ return tuple (BomRef (value = t ) for t in o )
160161
161162 @classmethod
162163 def xml_normalize (cls , o : Iterable [BomRef ], * ,
@@ -166,17 +167,17 @@ def xml_normalize(cls, o: Iterable[BomRef], *,
166167 if len (o ) == 0 :
167168 return None
168169 elem_s = XmlElement (f'{{{ xmlns } }}tools' if xmlns else 'tools' )
170+ tool_name = f'{{{ xmlns } }}tool' if xmlns else 'tool'
171+ ref_name = f'{{{ xmlns } }}ref' if xmlns else 'ref'
169172 elem_s .extend (
170- XmlElement (
171- f'{{{ xmlns } }}tool' if xmlns else 'tool' ,
172- {'ref' : t .value }
173- ) for t in o if t )
173+ XmlElement (tool_name , {ref_name : t .value }) \
174+ for t in o if t .value )
174175 return elem_s
175176
176177 @classmethod
177178 def xml_denormalize (cls , o : 'XmlElement' , * ,
178179 default_ns : Optional [str ],
179- ** __ : Any ) -> tuple [BomRef ]:
180+ ** __ : Any ) -> tuple [BomRef , ... ]:
180181 return tuple (BomRef (value = t .get ('ref' )) for t in o )
181182
182183
@@ -287,6 +288,58 @@ def __repr__(self) -> str:
287288 f' methods={ self .methods } , tools={ self .tools } >'
288289
289290
291+ class _IdentityRepositorySerializationHelper (serializable .helpers .BaseHelper ):
292+ """ THIS CLASS IS NON-PUBLIC API """
293+
294+ @classmethod
295+ def json_normalize (cls , o : Iterable [Identity ], * ,
296+ view : Optional [type ['serializable.ViewType' ]],
297+ ** __ : Any ) -> Optional [Any ]:
298+ o = tuple (o )
299+ if l := len (o ) == 0 :
300+ return None
301+ if view is SchemaVersion1Dot5 :
302+ if l >= 1 :
303+ warn (f'serialization omitted some identity evidences due to unsupported amount: { o !r} ' ,
304+ category = UserWarning , stacklevel = 0 )
305+ return json_loads (o [0 ].as_json (view )) # type:ignore[attr-defined]
306+ return tuple (json_loads (i .as_json (view )) for i in o ) # type:ignore[attr-defined]
307+
308+ @classmethod
309+ def json_deserialize (cls , o : Any ) -> tuple [Identity ]:
310+ if isinstance (o , list ):
311+ return tuple (Identity .from_json (i ) for i in o ) # type:ignore[attr-defined]
312+ return (Identity .from_json (o ),) # type:ignore[attr-defined]
313+
314+ @classmethod
315+ def xml_normalize (cls , o : Iterable [Identity ], * ,
316+ element_name : str ,
317+ view : Optional [type ['serializable.ViewType' ]],
318+ xmlns : Optional [str ],
319+ ** __ : Any ) -> Optional [XmlElement ]:
320+ o = tuple (o )
321+ if l := len (o ) == 0 :
322+ return None
323+ if view is SchemaVersion1Dot5 :
324+ if l >= 1 :
325+ warn (f'serialization omitted some identity evidences due to unsupported amount: { o !r} ' ,
326+ category = UserWarning , stacklevel = 0 )
327+ o = (o [0 ],)
328+ elem_s = XmlElement (f'{{{ xmlns } }}' if xmlns else '' )
329+ elem_s .extend (i .as_xml ( # type:ignore[attr-defined]
330+ view ,
331+ as_string = False ,
332+ element_name = element_name , xmlns = xmlns
333+ ) for i in o )
334+ return elem_s
335+
336+ @classmethod
337+ def xml_denormalize (cls , o : 'XmlElement' , * ,
338+ default_ns : Optional [str ],
339+ ** __ : Any ) -> Identity :
340+ return Identity .from_xml (o , default_ns ) # type:ignore[attr-defined,no-any-return]
341+
342+
290343@serializable .serializable_class
291344class Occurrence :
292345 """
@@ -586,7 +639,7 @@ def __init__(
586639
587640 @property
588641 @serializable .xml_array (serializable .XmlArraySerializationType .NESTED , 'frame' )
589- def frames (self ) -> 'List [CallStackFrame]' :
642+ def frames (self ) -> 'list [CallStackFrame]' :
590643 """
591644 Array of stack frames
592645 """
@@ -650,7 +703,7 @@ def __init__(
650703 @property
651704 @serializable .view (SchemaVersion1Dot5 )
652705 @serializable .view (SchemaVersion1Dot6 )
653- @serializable .xml_array ( serializable . XmlArraySerializationType . FLAT , 'identity' )
706+ @serializable .type_mapping ( _IdentityRepositorySerializationHelper )
654707 @serializable .xml_sequence (1 )
655708 def identity (self ) -> 'SortedSet[Identity]' :
656709 """
@@ -662,9 +715,9 @@ def identity(self) -> 'SortedSet[Identity]':
662715 @identity .setter
663716 def identity (self , identity : Union [Iterable [Identity ], Identity ]) -> None :
664717 self ._identity = SortedSet (
665- (Identity ,) # convert to iterable
718+ (identity ,)
666719 if isinstance (identity , Identity )
667- else identity # is iterable already
720+ else identity
668721 )
669722
670723 @property
0 commit comments