title: How do I send XML in the body of a POST using RobotFramework RESTInstance
body: <p>I am trying to send a POST request using RobotFramework where the body of the request is XML.</p>
  
  <p>Here is a Sample Robot file:</p>
  
  <pre><code>*** Settings ***
  Library    REST
  
  *** Variables ***
  ${SERVER_URL}    https://www.w3schools.com/xml/tempconvert.asmx
  
  *** Test Cases ***
  CL 1
      [Tags]    TC-CL
  #    Send SOAP via POST    &lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"&gt;" &lt;soap:Body&gt;" &lt;FahrenheitToCelsius xmlns="https://www.w3schools.com/xml/"&gt;" &lt;Fahrenheit&gt;100&lt;/Fahrenheit&gt;" &lt;/FahrenheitToCelsius&gt;" &lt;/soap:Body&gt;"&lt;/soap:Envelope&gt;
  #    Send SOAP via POST    &lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;&lt;soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"&gt; &lt;soap:Body&gt; &lt;FahrenheitToCelsius xmlns=\"https://www.w3schools.com/xml/\"&gt;\" &lt;Fahrenheit&gt;100&lt;/Fahrenheit&gt; &lt;/FahrenheitToCelsius&gt; &lt;/soap:Body&gt;&lt;/soap:Envelope&gt;
  #    Send SOAP via POST    &lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"&gt;\n &lt;soap:Body&gt;\n &lt;FahrenheitToCelsius xmlns=\"https://www.w3schools.com/xml/\"&gt;\n &lt;Fahrenheit&gt;100&lt;/Fahrenheit&gt;\n &lt;/FahrenheitToCelsius&gt;\n &lt;/soap:Body&gt;\n&lt;/soap:Envelope&gt;
      Send SOAP via POST    &lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"&gt;&lt;soap:Body&gt;&lt;FahrenheitToCelsius xmlns="https://www.w3schools.com/xml/"&gt;&lt;Fahrenheit&gt;100&lt;/Fahrenheit&gt;&lt;/FahrenheitToCelsius&gt;&lt;/soap:Body&gt;&lt;/soap:Envelope&gt;
  
  
  *** Keywords ***
  Send SOAP via POST
      [Arguments]         ${body}
      SET HEADERS    {"SOAPAction": "https://www.w3schools.com/xml/FahrenheitToCelsius","Content-Type": "text/xml","Cache-Control": "no-cache"}
  
      ${resp}=    POST    ${SERVER_URL}    data=${body}
      Log    ${resp}  repr=true
  </code></pre>
  
  <p>When you run this code I get the error:</p>
  
  <pre><code>This is not a JSON string:
  "data=&lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/
  soap/envelope/"&gt;&lt;soap:Body&gt;&lt;FahrenheitToCelsius xmlns="https://www.w3schools.com/xml/"&gt;&lt;Fahrenheit&gt;100&lt;/Fahrenheit&gt;&lt;/FahrenheitToCelsius&gt;&lt;/soap:Body&gt;&lt;/soap:Envelope&gt;"
  </code></pre>
  
  <p>FYI: The commented out versions of "Send SOAP via POST" are different ways I tried to get around the issue.</p>
  
  <p>Here is sample python code using requests to ensure my endpoint works (once I get around that pesky "This is not a JSON string issue":</p>
  
  <pre><code>import requests
  
  url = "https://www.w3schools.com/xml/tempconvert.asmx"
  
  payload = "&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\r\n&lt;soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"&gt;\r\n  &lt;soap:Body&gt;\r\n   &lt;FahrenheitToCelsius xmlns=\"https://www.w3schools.com/xml/\"&gt;\r\n     &lt;Fahrenheit&gt;100&lt;/Fahrenheit&gt;\r\n   &lt;/FahrenheitToCelsius&gt;\r\n  &lt;/soap:Body&gt;\r\n&lt;/soap:Envelope&gt;"
  payload = "&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;&lt;soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"&gt; &lt;soap:Body&gt; &lt;FahrenheitToCelsius xmlns=\"https://www.w3schools.com/xml/\"&gt;\" &lt;Fahrenheit&gt;100&lt;/Fahrenheit&gt; &lt;/FahrenheitToCelsius&gt; &lt;/soap:Body&gt;&lt;/soap:Envelope&gt;"
  payload = '&lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"&gt;&lt;soap:Body&gt;&lt;FahrenheitToCelsius xmlns="https://www.w3schools.com/xml/"&gt;&lt;Fahrenheit&gt;100&lt;/Fahrenheit&gt;&lt;/FahrenheitToCelsius&gt;&lt;/soap:Body&gt;&lt;/soap:Envelope&gt;'
  headers = {
      'SOAPAction': "\"https://www.w3schools.com/xml/FahrenheitToCelsius\"",
      'Content-Type': "text/xml",
      'Cache-Control': "no-cache",
      }
  
  response = requests.request("POST", url, data=payload, headers=headers)
  
  print(response.text)
  </code></pre>
  
  <p>The problem seems to be in the RESTInstance code <a href="https://github.com/asyrjasalo/RESTinstance/blob/master/src/REST/keywords.py" rel="nofollow noreferrer">here</a></p>
  
  <p>The initial issue which is what throws the error (see Commented line below):</p>
  
  <pre><code>@keyword
  def post(self, endpoint, body=None, timeout=None, allow_redirects=None,
           validate=True, headers=None):
      """Make a ``POST`` request call to a specified ``endpoint``.
      Example:
      POST "Mr Potato" to endpoint and ensure status code is 201
      | `POST`    | /users          | { "id": 11, "name": "Mr Potato" } |
      | `Integer` | response status | 201                               |
      """
      endpoint = self._input_string(endpoint)
      request = deepcopy(self.request)
      request['method'] = "POST"
      request['body'] = self.input(body)  # &lt;== ERROR THROWS HERE
      if allow_redirects is not None:
          request['allowRedirects'] = self._input_boolean(allow_redirects)
      if timeout is not None:
          request['timeout'] = self._input_timeout(timeout)
      validate = self._input_boolean(validate)
      if headers:
          request['headers'].update(self._input_object(headers))
      return self._request(endpoint, request, validate)['response']
  </code></pre>
  
  <p>The root issue is that the _request code forces body to be used in the json parameter of the requests.requests call (aka client based on import)  see code snippet from same file below:</p>
  
  <pre><code>def _request(self, endpoint, request, validate=True):
      if endpoint.endswith('/'):
          endpoint = endpoint[:-1]
      if not endpoint.startswith(('http://', 'https://')):
          base_url = self.request['scheme'] + "://" + self.request['netloc']
          if not endpoint.startswith('/'):
              endpoint = "/" + endpoint
          endpoint = urljoin(base_url, self.request['path']) + endpoint
      request['url'] = endpoint
      url_parts = urlparse(request['url'])
      request['scheme'] = url_parts.scheme
      request['netloc'] = url_parts.netloc
      request['path'] = url_parts.path
      try:
          response = client(request['method'], request['url'],
                            params=request['query'],
                            json=request['body'],    #&lt;== ROOT CAUSE HERE
                            headers=request['headers'],
                            proxies=request['proxies'],
                            cert=request['cert'],
                            timeout=tuple(request['timeout']),
                            allow_redirects=request['allowRedirects'],
                            verify=request['sslVerify'])
  </code></pre>
  
  <p>When sending XML using the requests library you need to use <strong>body=</strong> not <strong>json=</strong></p>