Following your code example, this might be the quickest workaround

 TEST>set x = ##class(test.msg.struct.TestXML).%New()
 TEST>set x.statusId = "111"
 TEST>set x.service = "222"
 TEST>do ##class(Ens.Util.JSON).ObjectToJSONStream(x, .obj1, "aelotu")
 TEST>write $REPLACE(obj1.Read(),"statusId","status_Id")
{       "status_Id":"111",       "service":"222"}

Not to pretty but useful.
 ​

very short:

.NET binding in Caché projects Caché Object classes to .NET with properties and methods
and services them with a private protocol. It is dependent on synchronized objects at both ends.

you may know this picture:

http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GBMP_intro

A RESTful API does something total different.

You define request (for data or actions)  and sent the request to Caché.
And you get it back preferred in JSON style expressed.
Your requests are straight HTTP with someWebServer in between on your way to Caché.

SOAP does something similar. But it has an incredible overhead of XML.
And as with .NET binding you need predefined data structures to be serviced.

In extrem:  With REST you just get back a string / stream and it is up to you to decide what this means:
No predefinded structures. It is all your task to organize yourself.
For .NET end I found this link:
https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/calling-a-web-api-from-a-net-client
 

For Caché side there are lots of useful articles in the forum already and examples in SAMPLES.

Simple summary:

For RESTapi you will not need 
InterSystems.Data.CacheClient.dll nor generated proxy classes using the Caché Object Binding Wizard for .NET
You are the owner of the exchanged data structures.
 

Are looking for this?

SELECT JSON_OBJECT('ID':ID,'Column1':COLUMN1,'Column2':COLUMN2,'DataID':DataID) FROM  SQLUser.SampleData

Cache for Windows (x86-64) 2017.2.1 (Build 801U)

Sorry I don't have your data to show your result

somewhat similar. 

SELECT top 3 JSON_OBJECT('name:':Name,'home':HOME_CITY,'dob':DOB) FROm Sample.Person

{"name:":"Smyth,Valery B.","home":"Hialeah","dob":"2001-01-06"}
{"name:":"Jones,Valery E.","home":"Islip","dob":"1959-11-11"}
{"name:":"Newton,David H.","home":"Youngstown","dob":"1969-12-01"}

Or all together:

SELECT '{"result":'||List(xx)||'}' FROM
(SELECT top 3 JSON_OBJECT('name:':Name,'home':HOME_CITY,'dob':DOB) xx
FROM Sample.Person)

{"result":
  {"name:":"Smyth,Valery B.","home":"Hialeah","dob":"01/06/2001"}
 ,{"name:":"Jones,Valery E.","home":"Islip","dob":"11/11/1959"}
 ,{"name:":"Newton,David H.","home":"Youngstown","dob":"12/01/1969"}
}
 

The difference in DOB is just display mode vs. ODBC mode 

Sorry you went to CLASSES   not to SQL !!! 
These are different worlds with different rules and syntax.

And while your classname is WINSURGEDMP.WINSURGERESULTFACT

I expect your TABLE to be named WINSURGE_DMP.WINSURGE_RESULT_FACT

It is just by accident if TABLEs and CLASSes have the same name !!!!!
Especially if you refer to an EXTERNAl TABLE in a different (non Caché) database

#1) check that there exists a valid class WINSURGE*...   

#2) in Mgmt Portal > System Explorer > SQL you should see as Table WINSURGE_DMP.WINSURGE_RESULT_FACT

#3) #SQLCompile Path=cancreg 
seems the real source of your problem as it sets a default package 
cancreg  wherever that may come from

http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCOS_macros#GCOS_macros_mpp_lbSQLCompile_Path

Eliminate it as your table is already full qualified  WINSURGE_DMP.WINSURGE_RESULT_FACT

In addition :  - VALUES :FIELD()
do you really write to ALL fields of the external table ??? 

Hint:

try to execute your SQL statement first from Mgmt Portal using dummy values before coding with strange macros directives

start with a simple statement to see if the connection works as expected:

SELECT COUNT(*) FROM WINSURGE_DMP.WINSURGE_RESULT_FACT

then try:

INSERT INTO WINSURGE_DMP.WINSURGE_RESULT_FACT VALUES (1,2,3,4,5,6)   -- whatever it needs

the WRITE is just to visualize the example

the loop in YOUR class could be: 

 for msg=1:1 set msg(msg)=stream.ReadLine() quit:stream.AtEnd 


but that's just standard COS programming. Nothing special. see here
Then you have the local array msg with your SOAP message
Similiar the Try{ } Catch {}  construct that ignores the error that you experience as you don't get a real SOAP response here.

You may create a normal SOAP Client in Studio using the Wizard.

Then you add a transport class to display / dump your request. (example in SAMPLES)

Class SOAPDemo.Transport Extends %RegisteredObject
{
ClassMethod DoSOAPRequest(
 client As %SOAP.WebClient,
 Action As %String,
 OneWay As %Boolean = 0,
 stream As %FileBinaryStream,
 ByRef responseStream As %GlobalBinaryStream) As %Status
{

    write !,"**** SOAP Request Begin ****",!
    for  write stream.ReadLine(),! quit:stream.AtEnd
    write !,"**** SOAP Request End ****",!
    quit 
$$$OK
}
}

Example to use it :

SAMPLES>set soap=##class(SOAP.DemoProxy).%New()
 
SAMPLES>set soap.Transport=##class(SOAPDemo.Transport).%New()  ;; add cusomized transport
 
SAMPLES>try {write !,soap.Mission() } catch {}    ;; you don't get a reply, so ignore it
  
**** SOAP Request Begin ****
<?xml version="1.0" encoding="UTF-8" ?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:s='http://www.w3.org/2001/XMLSchema'>  
<SOAP-ENV:Body><Mission xmlns="http://tempuri.org"></Mission></SOAP-ENV:Body>
</SOAP-ENV:Envelope>

**** SOAP Request End ****
 
SAMPLES>try {write !,soap.AddInteger(17,4) } catch {}
 
**** SOAP Request Begin ****
<?xml version="1.0" encoding="UTF-8" ?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:s='http://www.w3.org/2001/XMLSchema'>  
<SOAP-ENV:Body><AddInteger xmlns="http://tempuri.org"><Arg1>17</Arg1><Arg2>4</Arg2></AddInteger></SOAP-ENV:Body>
</SOAP-ENV:Envelope>
**** SOAP Request End ****

 

so you get Envelope + Body well separated

That makes it more clear, but not easier.

Class  %SOAP.WebClient  is the base for any SOAP Client in Caché.
It has a property Transport to provide a registered class for alternate transport (or no immediate transport in your case)

check the code starting with Method InvokeClient(...
and look for transport within the next 50 lines to see how that works up to here

#; Make the request
Set sc=transport.DoSOAPRequest($this,Action,OneWay,stream,.responseStream)


Just to be clear:
I personally won't do it that way because of all the related maintenance risks.