The problem I'm having is with Apache CXF putting the namespace inside the element instead of soap envelope.
I used codegen maven plugin to generate the client code from WSDL.
Here are the namespaces in the WSDL:
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
When I fire a SOAP request I can see in logs that the following message has been sent:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns1:userRegistrationRequest xmlns:ns1="http://some.com/namespace1">
<InputParameter xmlns:ns2="http://some.com/namespace1">
<ns2:Message>
<ns2:Value xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
<ns2:HashTable>
<ns2:Item key="action">
<ns2:Value>some_action</ns2:Value>
</ns2:Item>
</ns2:HashTable>
</ns2:Message>
</InputParameter>
</ns1:userRegistrationRequest>
</soap:Body>
The problem is that the Value element was null in my code when I constructed the message, but here it appears with XSI namespace.
What I was expecting to get is something like this (Value element should not be present and XSI should be in the envelope element):
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<ns1:userRegistrationRequest xmlns:ns1="http://some.com/namespace1">
<InputParameter xmlns:ns2="http://some.com/namespace1">
<ns2:Message>
<ns2:HashTable>
<ns2:Item key="action">
<ns2:Value>some_action</ns2:Value>
</ns2:Item>
</ns2:HashTable>
</ns2:Message>
</InputParameter>
</ns1:userRegistrationRequest>
</soap:Body>
Does anyone have an idea how to prevent the CXF from generating that empty Value element and put the namespace inside the soap envelope element?
Ok, so to answer my question. There are two aspects of this problem.
First, adding namespaces to the envelope element. It can be done by writting a custom interceptor and registering it in some early phase. Something like this:
import java.util.Map;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.Phase;
public class MyInterceptor extends AbstractSoapInterceptor {
private Map<String, String> nsMap;
public MyInterceptor(Map<String, String> nsMap) {
super(Phase.USER_LOGICAL);
this.nsMap = nsMap;
}
public void handleMessage(SoapMessage message) throws Fault {
message.put("soap.env.ns.map", nsMap);
message.put("disable.outputstream.optimization", Boolean.TRUE);
}
}
I found the above solution here
Secondly, to remove empty value element an xsd should be changed to that the element has minOccurs="0" and nillable = "false".
Related
I am trying to extract NetSuite saved search results using webservices. In the response I am only seeing the normal fields(entered in the Results section) but not the Formula (Text) field.
Below is the image of the saved search results section.
Below is the request sent to NetSuite.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="urn:platform_2014_2.webservices.netsuite.com" xmlns:platformMsgs="urn:messages_2014_2.platform.webservices.netsuite.com" xmlns:platformFaults="urn:faults_2014_2.platform.webservices.netsuite.com">
<soap:Header>
<urn5:preferences xmlns:urn5="urn:messages_2014_2.platform.webservices.netsuite.com">
<urn6:warningAsError xmlns:urn6="urn:messages_2014_2.platform.webservices.netsuite.com">false</urn6:warningAsError>
<urn7:disableMandatoryCustomFieldValidation xmlns:urn7="urn:messages_2014_2.platform.webservices.netsuite.com">false</urn7:disableMandatoryCustomFieldValidation>
<urn7:ignoreReadOnlyFields xmlns:urn7="urn:messages_2014_2.platform.webservices.netsuite.com">false</urn7:ignoreReadOnlyFields>
</urn5:preferences>
<urn8:searchPreferences xmlns:urn8="urn:messages_2014_2.platform.webservices.netsuite.com">
<urn9:bodyFieldsOnly xmlns:urn9="urn:messages_2014_2.platform.webservices.netsuite.com">true</urn9:bodyFieldsOnly>
<urn10:pageSize xmlns:urn10="urn:messages_2014_2.platform.webservices.netsuite.com">1000</urn10:pageSize>
<urn11:returnSearchColumns xmlns:urn11="urn:messages_2014_2.platform.webservices.netsuite.com">true</urn11:returnSearchColumns>
</urn8:searchPreferences>
</soap:Header>
<soap:Body>
<platformMsgs:search xmlns:platformMsgs="urn:messages_2014_2.platform.webservices.netsuite.com" xmlns="urn:messages_2014_2.platform.webservices.netsuite.com">
<platformMsgs:searchRecord xsi:type="ns1:CustomerSearchAdvanced" xmlns:ns1="urn:relationships_2014_2.lists.webservices.netsuite.com" savedSearchId="2469"/>
</platformMsgs:search>
</soap:Body>
The response I am seeing is below.
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<platformMsgs:documentInfo
xmlns:platformMsgs="urn:messages_2014_2.platform.webservices.netsuite.com">
<platformMsgs:nsId>WEBSERVICES_TSTDRV840553_091920161097968934683523704_57aaf0b</platformMsgs:nsId>
</platformMsgs:documentInfo>
</soapenv:Header>
<soapenv:Body>
<searchResponse
xmlns="urn:messages_2014_2.platform.webservices.netsuite.com">
<platformCore:searchResult
xmlns:platformCore="urn:core_2014_2.platform.webservices.netsuite.com">
<platformCore:status isSuccess="true"/>
<platformCore:totalRecords>2</platformCore:totalRecords>
<platformCore:pageSize>1000</platformCore:pageSize>
<platformCore:totalPages>1</platformCore:totalPages>
<platformCore:pageIndex>1</platformCore:pageIndex>
<platformCore:searchId>WEBSERVICES_TSTDRV840553_091920161097968934683523704_57aaf0b</platformCore:searchId>
<platformCore:searchRowList>
<platformCore:searchRow xsi:type="listRel:CustomerSearchRow"
xmlns:listRel="urn:relationships_2014_2.lists.webservices.netsuite.com">
<listRel:basic
xmlns:platformCommon="urn:common_2014_2.platform.webservices.netsuite.com">
<platformCommon:comments>
<platformCore:searchValue>test</platformCore:searchValue>
</platformCommon:comments>
<platformCommon:entityId>
<platformCore:searchValue>test c 97161</platformCore:searchValue>
</platformCommon:entityId>
<platformCommon:entityStatus>
<platformCore:searchValue internalId="13"/>
</platformCommon:entityStatus>
<platformCommon:internalId>
<platformCore:searchValue internalId="59951"/>
</platformCommon:internalId>
</listRel:basic>
</platformCore:searchRow>
<platformCore:searchRow xsi:type="listRel:CustomerSearchRow"
xmlns:listRel="urn:relationships_2014_2.lists.webservices.netsuite.com">
<listRel:basic
xmlns:platformCommon="urn:common_2014_2.platform.webservices.netsuite.com">
<platformCommon:comments>
<platformCore:searchValue>test</platformCore:searchValue>
</platformCommon:comments>
<platformCommon:entityId>
<platformCore:searchValue>test c 97162</platformCore:searchValue>
</platformCommon:entityId>
<platformCommon:entityStatus>
<platformCore:searchValue internalId="13"/>
</platformCommon:entityStatus>
<platformCommon:internalId>
<platformCore:searchValue internalId="59952"/>
</platformCommon:internalId>
</listRel:basic>
</platformCore:searchRow>
</platformCore:searchRowList>
</platformCore:searchResult>
</searchResponse>
</soapenv:Body>
I don't see any explanation/examples related to this in NetSuite help guide as well.
Is there any other way that we need to trigger the request for extracting formula fields? Any pointers would be greatly helpful.
Thanks
I've been looking into this same topic and I don't believe Web Services supports formula fields in the result set. There appears to be no mechanism for returning them in a statically defined SOAP response. In my case, I needed the following formula.
GREATEST({trandate},{lastmodifieddate},{linelastmodifieddate},{billingtransaction.trandate},{billingtransaction.lastmodifieddate},{billingtransaction.linelastmodifieddate})
It existed in my saved search and did not throw an error when referenced from SuiteTalk. However, the formula field was not present as a column in my result set. I had to include each individual field used in my formula in the saved search and then recreate the formula in code.
If you need to use a formula as part of your filter criteria (to apply to a saved search), it will need to be defined in the saved search itself. You will not be able to apply formula based filters via the SuiteTalk API.
TransactionSearchAdvanced customSearch = new TransactionSearchAdvanced()
{
savedSearchScriptId = "customsearch_[Your ID here]"
,
criteria = new TransactionSearch()
{
[Formula fields are NOT supported here]
}
};
My company has subscribed to a service offered by a travel reservation system whereby they call us back when any changes occur to a travel itinerary. My task is to stand up a SOAP endpoint to accept this callback. I am doing this with standard jax-ws stuff.
Here is the essentials of their soap message:
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:eb="http://www.ebxml.org/namespaces/messageHeader" xmlns:swse="http://wse.sabre.com/eventing"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wse="http://schemas.xmlsoap.org/ws/2004/08/eventing">
<soap-env:Header>
<eb:MessageHeader eb:version="1.0"
soap-env:mustUnderstand="1">
<wse:MySubscription>7TZA</wse:MySubscription>
<swse:EventTopic>WSE.QUEUE.CCC.PNRCHNG</swse:EventTopic>
</eb:MessageHeader>
<wsa:Action>http://wse.sabre.com/EventSource/notification</wsa:Action>
<wsa:MessageID>721d8dc0-1307-4b27-a48b-a9ba7f7818c7</wsa:MessageID>
<wse:Identifer>7f9aad13-a8cd-4057-8c91-89ccfad64598</wse:Identifer>
<wsa:To>http://localhost:18888/</wsa:To>
</soap-env:Header>
<soap-env:Body>
<swse:CCC.PNRCHNG>
<swse:OWNPCC>N0V3</swse:OWNPCC>
<swse:HOMEPCC>W0H3</swse:HOMEPCC>
<swse:Locator>IGLYIZ</swse:Locator>
<swse:EventTimeStamp format="yyyy-MM-dd hh:mm:ss.fffffffff">2007-10-30 11:41:32.000986</swse:EventTimeStamp>
<swse:ChangeIndicators>
<swse:Indicator name="Ticketing">
<swse:hasChanged>Y</swse:hasChanged>
</swse:Indicator>
...
</swse:CCC.PNRCHNG>
</soap-env:Body>
I have a functioning SOAPHandler with a getHeaders() implementation wich is satisfying the mustUnderstand=1 requirement.
I have a #WebMethod that is successfully accepting a few of the top level parts of the payload. This is actually good enough for now, but I would like to understand how to write a #Webmethod that would accept the whole payload as a complex object.
I have a jibx-generated CCC.PNRCHNG class. But how would I write the #WebMethod to accept it? Below is the method that accepts the top level bits as separate #WebParams (Locator is all I really need right now)
#WebMethod(operationName="CCC.PNRCHNG", action="http://wse.sabre.com/EventSource/notification")
public Object onPnrEvent(
#WebParam(name="OWNPCC", targetNamespace=NS) String ownPcc,
#WebParam(name="HOMEPCC", targetNamespace=NS) String homePcc,
#WebParam(name="Locator", targetNamespace=NS) String locator
) {
try {
s_logger.info(locator);
}
catch(Exception e) {
s_logger.error(e);
}
return null;
}
It would be nice to get the full model so any advice there would be very much appreciated.
Best way would be to generate PortType based on WSDL with wsimport. If you don't have a WSDL, I wouldn't bother with writing a wrapped JAX-WS service.
On your class, add this annotation
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
Then write methods that accept the root element. Something like this:
#WebMethod(operationName="CCC.PNRCHNG", action="http://wse.sabre.com/EventSource/notification")
public Object onPnrEvent( #WebParam(name="CCC.PNRCHNG", targetNamespace=NS) PNRCHNG request) {
}
I would appreciate any help with following:
XML Response:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns3:GetSeatPlanAvailabilityResponse xmlns:ns2="http://stagecoach.com/schemas/engine/common" xmlns:ns3="http://stagecoach.com/schemas/engine/seat">
<ns3:header>
<ns2:version>1.0</ns2:version>
</ns3:header>
<ns3:journeyDetails>
<ns2:journeyId>4769046</ns2:journeyId>
<ns2:origin>
<ns2:cityId>120</ns2:cityId>
<ns2:description>Memphis, TN</ns2:description>
<ns2:stop>MEM</ns2:stop>
</ns2:origin>
<ns2:destination>
<ns2:cityId>320</ns2:cityId>
<ns2:description>Austin, TX</ns2:description>
<ns2:stop>AUS</ns2:stop>
</ns2:destination>
<ns2:departureDate>2014-07-28Z</ns2:departureDate>
<ns2:departureTime>04:00:00Z</ns2:departureTime>
</ns3:journeyDetails>
<ns3:noOfNonReservableSeatsRemaining>70</ns3:noOfNonReservableSeatsRemaining>
<ns3:singleSeats>
<ns2:singleSeat>
<ns2:seatPlanKey>US1</ns2:seatPlanKey>
<ns2:seatId>7ecc7775-6caa-4e17-80d7-6cfa46be507b</ns2:seatId>
<ns2:seatNumber>1</ns2:seatNumber>
<ns2:seatClassCode>FRN</ns2:seatClassCode>
<ns2:seatDeck>UPPER</ns2:seatDeck>
<ns2:availableForSelection>true</ns2:availableForSelection>
</ns2:singleSeat>
<ns2:singleSeat>
<ns2:seatPlanKey>US2</ns2:seatPlanKey>
<ns2:seatId>fe73cc9a-7c4f-4d1f-80e5-6131926af694</ns2:seatId>
<ns2:seatNumber>2</ns2:seatNumber>
<ns2:seatClassCode>FRN</ns2:seatClassCode>
<ns2:seatDeck>UPPER</ns2:seatDeck>
<ns2:availableForSelection>true</ns2:availableForSelection>
</ns2:singleSeat>
<ns2:singleSeat>
<ns2:seatPlanKey>US3</ns2:seatPlanKey>
<ns2:seatId>20121517-d657-438f-bc26-92bb98b20bb5</ns2:seatId>
<ns2:seatNumber>3</ns2:seatNumber>
<ns2:seatClassCode>FRN</ns2:seatClassCode>
<ns2:seatDeck>UPPER</ns2:seatDeck>
<ns2:availableForSelection>true</ns2:availableForSelection>
</ns2:singleSeat>
<ns2:singleSeat>
<ns2:seatPlanKey>US4</ns2:seatPlanKey>
<ns2:seatId>b93be6b7-ebb9-482e-9ec2-3c9d32beab32</ns2:seatId>
<ns2:seatNumber>4</ns2:seatNumber>
<ns2:seatClassCode>FRN</ns2:seatClassCode>
<ns2:seatDeck>UPPER</ns2:seatDeck>
<ns2:availableForSelection>true</ns2:availableForSelection>
</ns2:singleSeat>
</ns3:singleSeats>
<ns3:doubleSeats/>
<ns3:salesClassInformation>
<ns2:seatClassCode>FRN</ns2:seatClassCode>
<ns2:salesClassCode>SFRN</ns2:salesClassCode>
<ns2:salesClassDescription/>
<ns2:price>3.00</ns2:price>
<ns2:maxPassengers>1</ns2:maxPassengers>
</ns3:salesClassInformation>
<ns3:salesClassInformation>
<ns2:seatClassCode>LEG</ns2:seatClassCode>
<ns2:salesClassCode>SLEG</ns2:salesClassCode>
<ns2:salesClassDescription/>
<ns2:price>7.00</ns2:price>
<ns2:maxPassengers>1</ns2:maxPassengers>
</ns3:salesClassInformation>
<ns3:salesClassInformation>
<ns2:seatClassCode>TBL</ns2:seatClassCode>
<ns2:salesClassCode>STBL</ns2:salesClassCode>
<ns2:salesClassDescription/>
<ns2:price>9.00</ns2:price>
<ns2:maxPassengers>1</ns2:maxPassengers>
</ns3:salesClassInformation>
</ns3:GetSeatPlanAvailabilityResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
XML Request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:bas="http://stagecoach.com/schemas/engine/basket" xmlns:com="http://stagecoach.com/schemas/engine/common">
<soapenv:Header/>
<soapenv:Body>
<bas:AddSeatToBasketRequest>
<bas:header>
<com:version>1.0</com:version>
<com:retailOperation>US</com:retailOperation>
<com:channel>WEB</com:channel>
</bas:header>
<bas:basketItemId>56227</bas:basketItemId>
<!--1 or more repetitions:-->
<bas:seatSelectionInput>
<com:passengerOrdinal>1</com:passengerOrdinal>
<com:SeatId>7ecc7775-6caa-4e17-80d7-6cfa46be507b</com:SeatId>
</bas:seatSelectionInput>
<bas:seatSelectionInput>
<com:passengerOrdinal>2</com:passengerOrdinal>
<com:SeatId>d653b812-3230-4a31-88e7-a4ad867fb131</com:SeatId>
</bas:seatSelectionInput>
</bas:AddSeatToBasketRequest>
</soapenv:Body>
</soapenv:Envelope>
My requirement is such that I need to pass <ns2:seatId> to another request, first it should look for condition when <ns2:availableForSelection> is TRUE, or else it should skip to 'FALSE' and select `and pass it to request
Could some one please provide groovy script for above logic?
You will need to adjust the below code for your exact needs, as there is still information missing in your question.
// to read a node from your Response
def grUtils = new com.eviware.soapui.support.GroovyUtils(context)
// depending on when / how your are doing this, you will need to provide the exact test step name
def xmlHolder = grUtils.getXmlHolder("${context.currentStep.name}#Response")
def seatIdNode = xmlHolder.getDomNode("//*:singleSeat[availableForSelection='true']/seatId")
// if nothing was found
if(seatIdNode == null) {
testRunner.cancel("seatId was not found!")
return
}
// you can store this in a testcase property
testRunner.testCase.setPropertyValue("seatId", seatId.firstChild.nodeValue)
// and use it in your next Request as '${#TestCase#seatId}'
There is a way to write the seatId directly into the next Request. If you wish to go that route, I have a blog entry that describes how to generate XML requests.
Lastly, all of this could also be done with pure SoapUI test steps, without the use of Groovy. A Conditional GoTo step would check if the XPath above exists, and bypass your next Request if it is not. A Transfer Property step using the same XPath as above would transfer the value of seatId to your next Request.
I have a webservice in C#.NET with the following namespace:
[WebService (Namespace = "http://enterpriseName/wsName")]
The web service contains a WebMethod GetServiceObject and a class MyObject.
This web method returns a string whose content is a serialized instance of MyObject.
[WebMethod (MessageName = "GetServiceObjectXML" Description = "Get ServiceObject from Server to Client")]
public string GetServiceObjectXML ()
This method returns the following XML:
<? Xml version = "1.0" encoding = "utf-16"?>
<ServiceObject Xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Services>
<service>
<id>3</id>
<date>02/08/2010</date>
</service>
</Services>
</ServiceObject>
The problem that I encounter is that when I call this method from the client side and try to deserialize this xml to class MyObject and I get a NULL object.
After that I created a new WebMethod with the following signature:
[WebMethod (MessageName = "GetServiceObject" Description = "Get ServiceObject from Server to Client")]
public MyObject GetServiceObject ()
When I call this method from the client side I get the object filled correctly and I can also serialize the object without problems, but the result of serialization is the following xml:
<? Xml version = "1.0" encoding = "utf-16"?>
<ServiceObject Xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Services Xmlns="http://enterpriseName/wsName">
<service>
<id>3</id>
<date>02/08/2010</date>
</service>
</Services>
</ServiceObject>
which is different from the xml generated by the WebMethod GetServiceObjectXML.
How can I get around this, since I intend to use both methods on the same webservice and in the same customer?
The obvious answer would be, fix GetServiceObjectXML() to return the same XML as GetServiceObject(). The difference seems to be that the object as serialized by the framework has a different XML namespace specified. Whatever method you're using to serialize the object into XML in GetServiceObjectXML() isn't doing that.
I would like to create a service that accepts a complex nested type. In a sample asmx file I created:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class ServiceNest : System.Web.Services.WebService
{
public class Block
{
[XmlElement(IsNullable = false)]
public int number;
}
public class Cell
{
[XmlElement(IsNullable = false)]
public Block block;
}
public class Head
{
[XmlElement(IsNullable = false)]
public Cell cell;
}
public class Nest
{
public Head head;
}
[WebMethod]
public void TakeNest(Nest nest)
{
}
}
When I view the asmx file in IE the test page shows the example SOAP post request as:
<?xml version="1.0" encoding="utf-8"?>
<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/">
<soap:Body>
<TakeNest xmlns="http://schemas.intellicorp.com/livecompare/">
<nest>
<head>
<cell>
<block xsi:nil="true" />
</cell>
</head>
</nest>
</TakeNest>
</soap:Body>
</soap:Envelope>
It hasn't expanded the <block> into its number member.
Looking at the WSDL, the types all look good. So is this just a limitation of the post demo page creator?
Thanks.
But those elements ARE null. You need to construct them before they show up otherwise they are just null.
As Kevin pointed out the example POST XML indicates that those elements are nil. I should have simply tried to consume the web service. Once I did that I could see that the importer (either .NET, Java or Ruby) correctly created all the types. So really there is no question here after all.
The .NET code did not give up after a certain number of levels.
If you look at the code generated by "Add Web Reference", you'll find that there's a bool numberSpecified field. Only if the client sets that to true will the number be serialized.
If you look at the XML Schema, you'll see that the number element might be absent. If it were of a reference type, then that could be represented in the client by a null value. Since it's an int, this additional flag is necessary to indicate whether or not to serialize this optional value.