'xml attributes instead child elements

Using djangorestframework-xml, can someone please help me how to render child elements as attributes?

Following the example below

<CreateCustomerAndMortgage>
  <Identifier>
    <UniqueID>182419002</UniqueID>
    <Type>BrokerAssigned</Type>
  </Identifier>
</CreateCustomerAndMortgage>

it should become

<CreateCustomerAndMortgage>
<Identifier UniqueID="182419002" Type="BrokerAssigned"/>
</CreateCustomerAndMortgage>

I was hoping I could setup something on the serializers instead overriding the XMLRenderer. Note: I know that attributes is not industry standards, but I have received the xsd already and unfortunately I'm not in power to change it.

This is my serializer

class Identifier(serializers.Serializer):
    unique_id = serializers.CharField()
    type = serializers.CharField()


Solution 1:[1]

You can achieve this using a custom renderer class overrides _to_xml method of XMLRenderer class, like this:

# renderers.py in example_app 
from django.utils.encoding import force_str
from rest_framework_xml.renderers import XMLRenderer


class XmlAttributesRenderer(XMLRenderer):
    """
    Renderer which serializes data to XML attributes.
    """

    def _to_xml(self, xml, data):
        # render the data to xml how you'd like
        # ...
        # use xml.addQuickElement('name_of_xml_tag', attrs={'attr1': 'value_of_attr1'})
        has_xml_tag = False
        if isinstance(data, (list, tuple)):
            for item in data:
                has_xml_tag = True
                xml.addQuickElement(self.item_tag_name, attrs=self._to_xml(xml, item))

        elif isinstance(data, dict):
            dict_data = dict(map(lambda di: (force_str(di[0]), force_str(self._to_xml(xml, di[1]))), data.items()))
            if has_xml_tag:
                return dict_data
            else:
                has_xml_tag = True
                xml.addQuickElement('data', attrs=dict_data)

        elif data is None:
            return ''
        else:
            return force_str(data)

        if not has_xml_tag:
            xml.addQuickElement('data', attrs={})

and use the custom renderer in the settings.py:

REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': (
        'rest_framework_xml.parsers.XMLParser',
    ),
    'DEFAULT_RENDERER_CLASSES': (
        'example_app.renderers.XmlAttributesRenderer',
    ),
}

or in you view class:

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    renderer_classes = (XmlAttributesRenderer,)

sample responses:

/api/users

<root>
<list-item email="[email protected]" is_staff="True" url="http://127.0.0.1:8000/api/users/1/" username="admin"/>
</root>

/api/users/1

<root>
<data email="[email protected]" is_staff="True" url="http://127.0.0.1:8000/api/users/1/" username="admin"/>
</root>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1