Skip to content

SOAP Renderer implementation #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 61 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# REST Framework XML

[![build-status-image]][github-action]
[![build-status-image]][travis]
[![pypi-version]][pypi]

**XML support for Django REST Framework**
Expand All @@ -15,9 +15,11 @@ XML support extracted as a third party package directly from the official Django

## Requirements

* Python 3.5+
* Django 2.2+
* Django REST Framework 3.11+
* Python (2.7, 3.4, 3.5, 3.6)
* Django (1.8 - 1.11, 2.0 - 2.1)
* Django REST Framework (2.4, 3.0 - 3.9)

This project is tested on the combinations of Python and Django that are supported by each version of Django REST Framework.

## Installation

Expand Down Expand Up @@ -75,15 +77,68 @@ class UserViewSet(viewsets.ModelViewSet):
</root>
```

## SOAP Renderer

You can also set the SOAP renderer for an individual view, or viewset, using the APIView class based views. You must reload soap schema.

```python
from rest_framework import routers, serializers, viewsets
from rest_framework_xml.parsers import XMLParser
from rest_framework_xml.renderers import SOAPRenderer


class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email', 'is_staff')


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

soap_tag = "SOAP-TEST"
soap_endpoint = "https://xml.com/soap"
soap_service = "soapService"

renderer = SOAPRenderer
renderer.set_schema_attrs(soap_tag, soap_endpoint, soap_service)
renderer_classes = (renderer,)
```

### Sample output

```xml
<?xml version="1.0" encoding="utf-8"?>\n
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:dummyService="http://dummyservice.com/endpoint">
<SOAP-ENV:Header></SOAP-ENV:Header>
<SOAP-ENV:Body>
<dummyService:Response>
<TextSearch>Some words</TextSearch>
<ComparsionResult>
<ResultId>1</ResultId>
<ResultTag>tag one</ResultTag>
<ResultId>2</ResultId>
<ResultTag>tag two</ResultTag>
</ComparsionResult>
<RecordDate>2020-04-14 12:45:00</RecordDate>
</dummyService:Response>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
```

## Documentation & Support

Full documentation for the project is available at [http://jpadilla.github.io/django-rest-framework-xml][docs].

You may also want to follow the [author][jpadilla] on Twitter.


[build-status-image]: https://github.com/jpadilla/django-rest-framework-xml/workflows/CI/badge.svg
[github-action]: https://github.com/jpadilla/django-rest-framework-xml/actions?query=workflow%3ACI
[build-status-image]: https://secure.travis-ci.org/jpadilla/django-rest-framework-xml.svg?branch=master
[travis]: http://travis-ci.org/jpadilla/django-rest-framework-xml?branch=master
[pypi-version]: https://img.shields.io/pypi/v/djangorestframework-xml.svg
[pypi]: https://pypi.python.org/pypi/djangorestframework-xml
[defusedxml]: https://pypi.python.org/pypi/defusedxml
Expand Down
14 changes: 14 additions & 0 deletions docs/renderers.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,17 @@ If you are considering using `XML` for your API, you may want to consider implem
**item_tag_name**: `list-item`

**.root_tag_name**: `root`

## SOAP Renderer

If you are considering using `SOAP` renderer for your API, you may must need to create your own custom SOAP envelope structure.

**soap_tag**: `SOAP-TEST`
**soap_endpoint**: `https://xml.com/soap`
**soap_service**: `soapService`

For creating your SOAP envelope structure you must need to reload the schema:

renderer = SOAPRenderer
renderer.set_schema_attrs(soap_tag, soap_endpoint, soap_service)
renderer_classes = (renderer,)
90 changes: 84 additions & 6 deletions rest_framework_xml/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ class XMLRenderer(BaseRenderer):
Renderer which serializes to XML.
"""

media_type = "application/xml"
format = "xml"
charset = "utf-8"
item_tag_name = "list-item"
root_tag_name = "root"
media_type = 'application/xml'
format = 'xml'
charset = 'utf-8'
item_tag_name = 'list-item'
root_tag_name = 'root'

def render(self, data, accepted_media_type=None, renderer_context=None):
"""
Renders `data` into serialized XML.
"""
if data is None:
return ""
return ''

stream = StringIO()

Expand Down Expand Up @@ -57,3 +57,81 @@ def _to_xml(self, xml, data):

else:
xml.characters(force_str(data))


class SOAPRenderer(BaseRenderer):
"""
Renderer which serialize to SOAP Envelope
"""
media_type = 'application/xml'
format = 'xml'
charset = 'utf-8'

soap_tag = 'SOAP-ENV'
service_endpoint = 'http://dummyservice.com/endpoint'
service_name = 'dummyService'

schema_attrs = {
'xmlns:%s' % soap_tag: 'http://schemas.xmlsoap.org/soap/envelope/',
'xmlns:%s' % service_name: '%s' % service_endpoint
}

envelope_tag_name = '%s:Envelope' % soap_tag
header_tag_name = '%s:Header' % soap_tag
body_tag_name = '%s:Body' % soap_tag
response_tag_name = '%s:Response' % service_name

@classmethod
def set_schema_attrs(cls, soap_tag, service_endpoint, service_name):
cls.envelope_tag_name = '%s:Envelope' % soap_tag
cls.header_tag_name = '%s:Header' % soap_tag
cls.body_tag_name = '%s:Body' % soap_tag
cls.response_tag_name = '%s:Response' % service_name

cls.schema_attrs = {
'xmlns:%s' % soap_tag: 'http://schemas.xmlsoap.org/soap/envelope/',
'xmlns:%s' % service_name: '%s' % service_endpoint
}

def render(self, data, accepted_media_type=None, renderer_context=None):
"""
Renders `data` into serialized SOAP Envelope.
"""
if not data:
return ''

stream = StringIO()

xml = SimplerXMLGenerator(stream, self.charset)
xml.startDocument()

xml.startElement(self.envelope_tag_name, self.schema_attrs)
xml.addQuickElement(self.header_tag_name)
xml.startElement(self.body_tag_name, {})
xml.startElement(self.response_tag_name, {})

self._to_xml(xml, data)

xml.endElement(self.response_tag_name)
xml.endElement(self.body_tag_name)
xml.endElement(self.envelope_tag_name)

return stream.getvalue()

def _to_xml(self, xml, data):
if isinstance(data, (list, tuple)):
for item in data:
self._to_xml(xml, item)

elif isinstance(data, dict):
for key, value in data.items():
xml.startElement(key, {})
self._to_xml(xml, value)
xml.endElement(key)

elif data is None:
# Don't output any value
pass

else:
xml.characters(force_str(data))
Loading