Always return XML content

If you want all methods of a given interface to return XML content instead of JSON, you can set TServiceFactoryServer.ResultAsXMLObject to true.

Instead of this JSON array content, returned by default:

GET root/Calculator/Add?n1=1&n2=2
 ...
{"result":[3]}

or this JSON object, if ServiceFactoryServer.ResultAsJSONObject is true:

GET root/Calculator/Add?n1=1&n2=2
 ...
{"result":{"Result":3}}

The following XML will be returned if TServiceFactoryServer.ResultAsXMLObject is true:

GET root/Calculator/Add?n1=1&n2=2
 ...
<?xml version="1.0" encoding="UTF-8"?>
<result><Result>3</Result></result>

Conversion is processed from the JSON content generated by the mORMot kernel, via a call to JSONBufferToXML() function, which performs the XML generation without almost no memory allocation. So only a slightly performance penalty may be noticed (much faster in practice than most node-based XML producers available).

One drawback of using this TServiceFactoryServer.ResultAsXMLObject property is that your regular Delphi or AJAX clients won't be able to consume the service any more, since they expect JSON content.
If you want your service to be consumed either by XML and JSON, you would need two services. You can therefore define a dedicated interface to return XML, and then register this interface to return only XML:

type
  ICalculator = interface(IInvokable)
    ['{9A60C8ED-CEB2-4E09-87D4-4A16F496E5FE}']
    /// add two signed 32 bit integers
    function Add(n1,n2: integer): integer;
  end;
  ICalculatorXML = interface(ICalculator)
    ['{0D682D65-CE0F-441B-B4EC-2AC75E357EFE}']
  end; // no additional method, just new name and GUID

TServiceCalculator = class(TInterfacedObject, ICalculator,ICalculatorXML) public // implementation class should implement both interfaces function Add(n1,n2: integer): integer; end;
... aServer.ServiceRegister(TServiceCalculator,[TypeInfo(ICalculator)],sicShared); aServer.ServiceRegister(TServiceCalculator,[TypeInfo(ICalculatorXML)],sicShared). ResultAsXMLObject := True; ...

There would therefore be two running service instances (e.g. here two instances of TServiceCalculator, one for ICalculator and one for ICalculatorXML). It could be an issue, in some cases.

And such a dedicated interface may need more testing and code on the server side, since they will be accessible from two URIs:

GET root/Calculator/Add?n1=1&n2=2
 ...
{"result":{"Result":3}}

and for ICalculatorXML interface:

GET root/CalculatorXML/Add?n1=1&n2=2
 ...
<?xml version="1.0" encoding="UTF-8"?>
<result><Result>3</Result></result>

Return XML content on demand

As an alternative, you can let the mORMot server inspect the incoming HTTP headers, and return the content as XML if the "Accept: " header is exactly "application/xml" or "text/xml".

You can set the TServiceFactoryServer.ResultAsXMLObjectIfAcceptOnlyXML property to enable this HTTP header detection:

  aServer.ServiceRegister(TServiceCalculator,[TypeInfo(ICalculator)],sicShared).
    ResultAsXMLObjectIfAcceptOnlyXML := true;

For standard requests, the incoming HTTP header will be either void, either "Accept: */*", so will return JSON content.
But if the client set either "Accept: application/xml" or "Accept: text/xml" in its header, then it will return an XML document.

Instead of this JSON content:

GET root/Calculator/Add?n1=1&n2=2
Accept: */*
 ...
{"result":{"Result":3}}

The following XML will be returned:

GET root/Calculator/Add?n1=1&n2=2
Accept: application/xml
 ...
<?xml version="1.0" encoding="UTF-8"?>
<result><Result>3</Result></result>

as it would with "text/xml":

GET root/Calculator/Add?n1=1&n2=2
Accept: text/xml
 ...
<?xml version="1.0" encoding="UTF-8"?>
<result><Result>3</Result></result>

Note that the header is expected to be "Accept: application/xml" or "Accept: text/xml" as exact value.
For instance "Accept: text/html,application/xml,*/*" won't be detected by the server, and will return regular JSON:

GET root/Calculator/Add?n1=1&n2=2
Accept: text/html,application/xml,*/*
 ...
{"result":{"Result":3}}

Your XML client should therefore be able to force the exact content of the HTTP "Accept:" header.

Together with parameter values optionally encoded at URI level - available with TSQLRestRoutingREST default routing scheme (see ?n1=1&n2=2 above)- it could be an useful alternative to consume mORMot services from any XML-based client.

Feedback is welcome on our forum, as usual!