[SalesForce] Problem with wsdl2apex and namespaces in the the callout body

I've been trying to get a webservice callout working by parsing it with wsdl2apex and constructing it with that method. Callouts are failing because of errors in the constructed XML which causes an exception to be thrown on the receiving end. I've narrowed it down to namespace issues, namely that the endpoint expects that one of the elements in the xml should have a namespace, which it doesn't when I inspect the xml being sent in my developer org via Developer Console.

This is a part of the SOAP service being referenced:

<xs:complexType name="faultReport">
<xs:sequence>
<xs:element name="serviceNumber" form="unqualified" type="xs:int"/>
<xs:element name="customerID" form="unqualified" type="xs:string" minOccurs="0"/>
<xs:element name="customerName" form="unqualified" type="xs:string" minOccurs="0"/>
<xs:element name="customerContact" form="unqualified" type="tns:contact" minOccurs="0"/>
<xs:element name="operatorContact" form="unqualified" type="tns:contact" minOccurs="0"/>
<xs:element name="description" form="unqualified" type="xs:string" minOccurs="0"/>
<xs:element name="measurement" form="unqualified" type="xs:string" minOccurs="0"/>
<xs:element name="origin" form="unqualified" type="xs:string" minOccurs="0"/>
<xs:element name="afterHours" form="unqualified" type="xs:string" minOccurs="0"/>
<xs:element name="user" form="unqualified" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>

This is my generated class:

 public class fault {
        public String customerSSN;
        public String customerName;
        public WSGateWay_Milatypes.contact customerContact;
        public WSGateWay_Milatypes.contact operatorContact;
        public WSGateWay_Milatypes.resolution resolution;
        public String origin;
        public DateTime closed;
        public String measurement;
        public WSGateWay_Milatypes.noteList notes;
        private String[] customerSSN_type_info = new String[]{'customerSSN','http://xxxx.xx/GateWay/XSD/Mila/MilaTypes/v1.0',null,'0','1','false'};
        private String[] customerName_type_info = new String[]{'customerName','http://xxxx.xx/GateWay/XSD/Mila/MilaTypes/v1.0',null,'0','1','false'};
        private String[] customerContact_type_info = new String[]{'customerContact','http://xxxx.xx/GateWay/XSD/Mila/MilaTypes/v1.0',null,'0','1','false'};
        private String[] operatorContact_type_info = new String[]{'operatorContact','http://xxxx.xx/GateWay/XSD/Mila/MilaTypes/v1.0',null,'0','1','false'};
        private String[] resolution_type_info = new String[]{'resolution','http://xxxx.xx/GateWay/XSD/Mila/MilaTypes/v1.0',null,'0','1','false'};
        private String[] origin_type_info = new String[]{'origin','http://xxxx.xx/GateWay/XSD/Mila/MilaTypes/v1.0',null,'0','1','false'};
        private String[] closed_type_info = new String[]{'closed','http://xxxx.xx/GateWay/XSD/Mila/MilaTypes/v1.0',null,'0','1','false'};
        private String[] measurement_type_info = new String[]{'measurement','http://xxxx.xx/GateWay/XSD/Mila/MilaTypes/v1.0',null,'0','1','false'};
        private String[] notes_type_info = new String[]{'notes','http://xxxx.xx/GateWay/XSD/Mila/MilaTypes/v1.0',null,'0','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://xxxx.xx/GateWay/XSD/Mila/MilaTypes/v1.0','true','false'};
        private String[] field_order_type_info = new String[]{'customerSSN','customerName','customerContact','operatorContact','resolution','origin','closed','measurement','notes'};
    }

This is my generated class for the Contact object, which is referenced in the class:

public class contact {
        public String name;
        public String number_x;
        private String[] name_type_info = new String[]{'name','http://xxxxx.xx/Gateway/XSD/Mila/MilaTypes/v1.0',null,'0','1','false'};
        private String[] number_x_type_info = new String[]{'number','http://xxxxx.xx/Gateway/XSD/Mila/MilaTypes/v1.0',null,'0','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://xxxx.xx/Gateway/XSD/Mila/MilaTypes/v1.0','true','false'};
        private String[] field_order_type_info = new String[]{'name','number_x'};
    }

My callout class then creates a stub from the webservice definition and makes the callout:

This is my example constructed callout, taken from the Developer Console logs:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Header/>
<env:Body>
    <faultReportRequest xmlns="http://xxxx.xx/Gateway/XSD/WebServices/FaultService/v1.5">
    <fault>
        <serviceNumber xmlns="">1234567</serviceNumber>
        <customerID xmlns="">302</customerID>
        <customerContact xmlns="">
            <name>Contact 1</name>
            <number>8629001</number>
        </customerContact>
        <operatorContact xmlns="">
            <name>Contact 2</name>
            <number>8629001</number>
        </operatorContact>
        <description xmlns="">something</description>
        <measurement xmlns="">measure</measurement>
        <origin xmlns="">Test</origin>
        <afterHours xmlns="">false</afterHours>
        <user xmlns="">Admin</user>
    </fault>
</faultReportRequest>
</env:Body>
</env:Envelope>

The problem is that the name and number elements under the Contact element should have a declared namespace, but they don't and instead the Contact element has a declared namespace, which it shouldn't. Can anyone shine a light on this, what string values I need to change to make this:

<customerContact xmlns="">
                <name>Contact 1</name>
                <number>8629001</number>
            </customerContact>

into this:

<customerContact xmlns="">
            <name xmlns="http://xxxx.xx/Gateway/XSD/WebServices/FaultService/v1.5">Contact 1</name>
            <number xmlns="http://xxxx.xx/Gateway/XSD/WebServices/FaultService/v1.5">8629001</number>
        </customerContact>

?

Using HTTPRequest to do this

Is it perhaps more feasible to do this by hand constructing the XML and making a HTTPrequest to the endpoint? It would be infinately easier, but my tests have all failed, since ehen I set the request endpoint as the endpoint referenced in the WSDL, it fails with a 'endpoint not found' error. Are there any huge gotchas with using that approach?

Edit

I'd like to add that I gave up on this eventually and had a REST based service created for me, and it took me all of 1 hour to get the connection to that up and running. Since Apex has a well documented and robust support for that I'd steer anyone in that direction if possible and avoid SOAP based integrations unless nothing else is available.

Best Answer

I worked with your WSDL and found out that it has <xsd:extension base="tns:Sample"> element in it which Salesforce.com does not handle. It is based on inheritance. We have uploaded a new version of SFDC Explorer (v1.3), which will now be able to parse and generate Apex classes with your WSDL.

After deploying the generated classes to Salesforce.com and when trying to invoke web service this is what the SOAP Request message looked like:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <env:Header />
  <env:Body>
    <faultReportRequest xmlns="http://siminn.is/MilaGateway/XSD/WebServices/FaultService/v1.5">
      <fault>
        <serviceNumber xmlns="http://siminn.is/MilaGateway/XSD/Mila/MilaTypes/v1.0">1</serviceNumber>
        <customerID xmlns="http://siminn.is/MilaGateway/XSD/Mila/MilaTypes/v1.0">xyz</customerID>
        <customerContact xmlns="http://siminn.is/MilaGateway/XSD/Mila/MilaTypes/v1.0">
          <name>bla</name>
          <number>bla</number>
        </customerContact>
      </fault>
    </faultReportRequest>
  </env:Body>
</env:Envelope>

I don't think <name> and <number> need namespace because they are strings according to the WSDL. They will take the namespace from their parent element.

Related Topic