Source code for relaton.serializers.bibxml

"""Serialization of :class:`relaton.models.bibdata.BibliographicItem`
into BibXML (xml2rfc) format roughly per RFC 7991,
with bias towards existing xml2rfc documents where differs.

Primary API is :func:`.serialize()`.

.. seealso:: :mod:`~relaton.serializers.bibxml_string`
"""

from typing import List
from xml.etree.ElementTree import Element
from lxml import objectify, etree

from ...util import as_list
from ...models.bibdata import BibliographicItem, Relation

from .reference import create_reference, create_referencegroup
from .anchor import get_suitable_anchor
from .target import get_suitable_target


__all__ = (
    'serialize',
)


[docs]def serialize(item: BibliographicItem, anchor: str = None) -> Element: """Converts a BibliographicItem to XML, trying to follow RFC 7991. Returned root element is either a ``<reference>`` or a ``<referencegroup>``. :param str anchor: resulting root element ``anchor`` property. :raises ValueError: if there are different issues with given item’s structure that make it unrenderable per RFC 7991. """ titles = as_list(item.title or []) relations: List[Relation] = as_list(item.relation or []) constituents = [rel for rel in relations if rel.type == 'includes'] is_referencegroup = len(titles) < 1 and len(constituents) > 0 is_reference = len(titles) > 0 if is_reference: root = create_reference(item) elif is_referencegroup: root = create_referencegroup([ ref.bibitem for ref in constituents]) else: raise ValueError( "Able to construct neither <reference> nor <referencegroup>: " "impossible combination of titles and relations") # Fill in default root element anchor, unless specified if anchor is None: try: anchor = get_suitable_anchor(as_list(item.docid or [])) except ValueError: pass if anchor: root.set('anchor', anchor) # Fill in appropriate target try: target = get_suitable_target(as_list(item.link or [])) except ValueError: pass else: root.set('target', target) objectify.deannotate(root) etree.cleanup_namespaces(root) return root