XML serialize helpers should also support iterables of elements
- Dominant language
- Python
- Stars
- 7
- Forks
- 6
- PR merge metrics
- No merged PRs in 30d
Description
py-serializable version: 2.0.0
python version: 3.13.3
os: linux
## What is the problem?
I try to implement the missing identity element of [componentEvidenceType](https://cyclonedx.org/docs/1.6/xml/#type_componentEvidenceType) into [cyclonedx-python-lib](https://github.com/CycloneDX/cyclonedx-python-lib). Since version 1.6 of the schema the identity has `maxOccurs="unbounded"` that means something like this is possible in 1.6:
~~~xml
purl
name
~~~
I am developing now a helper with a custom `xml_normalize` function. The problem I have now is that only:
- None
- a single Element
- a string which is used as value
are supported:
https://github.com/madpah/serializable/blob/bb035d25f6efabb959c1250af10d49c8c19d0ff1/py_serializable/__init__.py#L508-L515
## How to improve this?
It would be great to support also `List[Element]`. Maybe something like that:
~~~python
elif isinstance(ver_ser, List):
for element in ver_ser:
this_e.append(element)
~~~
With this simple approach something like this is possible:
~~~python
988 @classmethod
989 def xml_normalize(
990 cls, o: SortedSet[Identity], *,
991 element_name: str,
992 view: Optional[type['ViewType']],
993 xmlns: Optional[str],
994 **__: Any
~ 995 ) -> Optional[list[Element]]:
~ 996 if view is not None:
+ 997 schema_version: BaseSchemaVersion = cast(BaseSchemaVersion, view())
+ 998 if schema_version.schema_version_enum < SchemaVersion.V1_6 and len(o) > 1:
+ 999 raise SerializationOfUnexpectedValueException(
+ 1000 "The schema version 1.5 only supports a single identity object"
+ 1001 )
+ 1002 else:
+ 1003 return [
E 1004 identity.as_xml(
+ 1005 view_=view,
+ 1006 as_string=False,
+ 1007 element_name=element_name,
+ 1008 xmlns=xmlns,
+ 1009 )
+ 1010 for identity in o
+ 1011 ]
+ 1012 return None
~~~
~~~python
>>> from cyclonedx.schema.schema import SchemaVersion1Dot4, SchemaVersion1Dot5, SchemaVersion1Dot6
>>> from cyclonedx.model.component import ComponentEvidence, Identity, IdentityField
>>> foo = ComponentEvidence(identity=Identity(field=IdentityField.PURL))
>>> foo.as_xml(view_=SchemaVersion1Dot6)
'purl'
>>> foo.as_xml(view_=SchemaVersion1Dot5)
'purl'
>>> bar = ComponentEvidence(identity=[Identity(field=IdentityField.PURL), Identity(field=IdentityField.NAME)])
>>> bar.as_xml(view_=SchemaVersion1Dot6)
'namepurl'
>>> bar.as_xml(view_=SchemaVersion1Dot5)
Traceback (most recent call last):
File "", line 1, in
bar.as_xml(view_=SchemaVersion1Dot5)
~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jloehel/projects/github.com/jloehel/cyclonedx-python-lib/.venv/lib64/python3.13/site-packages/py_serializable/__init__.py", line 508, in as_xml
v_ser = prop_info.custom_type.xml_normalize(
v, view=view_, element_name=new_key, xmlns=xmlns, prop_info=prop_info, ctx=self.__class__)
File "/home/jloehel/projects/github.com/jloehel/cyclonedx-python-lib/cyclonedx/model/component.py", line 999, in xml_normalize
raise SerializationOfUnexpectedValueException(
"The schema version 1.5 only supports a single identity object"
)
cyclonedx.exception.serialization.SerializationOfUnexpectedValueException: The schema version 1.5 only supports a single identity object
~~~
edit: Maybe it's better to consider the helper for prop_info.is_array because
~~~python
@serializable.xml_array(serializable.XmlArraySerializationType.FLAT, child_name='identity')
~~~
is doing the right thing. I just wanna raise an exception in a special case.
Contributor guide
Research direction
Start with py_serializable/__init__.py around the as_xml logic at lines 508-515, then inspect the xml_array decorator behavior mentioned in the issue. Reproduce serialization with a single Element and a list or other iterable, and verify that supported iterable values produce flat child elements while the existing scalar behavior remains unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- tooling
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100