Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

These are running notes as I study how OLX is generated and parsed. Eventually, I want to clean this up and turn it into an official OLX Specification.

Table of Contents
stylenone

Relevant code

repo: openedx/XBlock

xblock/core.py

Code Block
breakoutModewide
languagepy
class Blocklike(...):

    @classmethod
    def parse_xml(cls, node, runtime, keys):
        """
        Use `node` to construct a new block.

        Arguments:
            node (:class:`~xml.etree.ElementTree.Element`): The xml node to parse into an xblock.
            runtime (:class:`.Runtime`): The runtime to use while parsing.
            keys (:class:`.ScopeIds`): The keys identifying where this block
                will store its data.
        """
        block = runtime.construct_xblock_from_class(cls, keys)

        # The base implementation: child nodes become child blocks.
        # Or fields, if they belong to the right namespace.
        for child in node:
            if child.tag is etree.Comment:
                continue
            qname = etree.QName(child)
            tag = qname.localname
            namespace = qname.namespace

            if namespace == XML_NAMESPACES["option"]:
                cls._set_field_if_present(block, tag, child.text, child.attrib)
            else:
                block.runtime.add_node_as_child(block, child)

        # Attributes become fields.
        for name, value in list(node.items()):  # lxml has no iteritems
            cls._set_field_if_present(block, name, value, {})

        # Text content becomes "content", if such a field exists.
        if "content" in block.fields and block.fields["content"].scope == Scope.content:
            text = node.text
            if text:
                text = text.strip()
                if text:
                    block.content = text

        return block
        
        ...
        
            def add_xml_to_node(self, node):
        """
        For exporting, set data on `node` from ourselves.
        """
        # pylint: disable=E1101
        # Set node.tag based on our class name.
        node.tag = self.xml_element_name()
        node.set('xblock-family', self.entry_point)

        # Set node attributes based on our fields.
        for field_name, field in list(self.fields.items()):
            if field_name in ('children', 'parent', 'content'):
                continue
            if field.is_set_on(self) or field.force_export:
                self._add_field(node, field_name, field)

        # A content field becomes text content.
        text = self.xml_text_content()
        if text is not None:
            node.text = text

    def xml_element_name(self):
        """
        What XML element name should be used for this block?
        """
        return self.scope_ids.block_type

    def xml_text_content(self):
        """
        What is the text content for this block's XML node?
        """
        if 'content' in self.fields and self.content:  # pylint: disable=unsupported-membership-test
            return self.content
        else:
            return None

    def _add_field(self, node, field_name, field):
        """
        Add xml representation of field to node.

        Depending on settings, it either stores the value of field
        as an xml attribute or creates a separate child node.
        """
        value = field.to_string(field.read_from(self))
        text_value = "" if value is None else value

        # Is the field type supposed to serialize the fact that the value is None to XML?
        save_none_as_xml_attr = field.none_to_xml and value is None
        field_attrs = {"none": "true"} if save_none_as_xml_attr else {}

        if save_none_as_xml_attr or field.xml_node:
            # Field will be output to XML as an separate element.
            tag = etree.QName(XML_NAMESPACES["option"], field_name)
            elem = etree.SubElement(node, tag, field_attrs)
            if field.xml_node:
                # Only set the value if forced via xml_node;
                # in all other cases, the value is None.
                # Avoids an unnecessary XML end tag.
                elem.text = text_value
        else:
            # Field will be output to XML as an attribute on the node.
            node.set(field_name, text_value)
            
...
    
class XBlock(Blocklike, ...):

    ...

repo: edx-platform