QXmlStreamWriter, namespace and prefix - c++

I am new to XML and trying to write the following xml using QXmlStreamWriter :
<S:Envelope xmlns:S="url1">
<S:Body>
<ns2:name1 xmlns:ns2="url2">
..
So far I have tried:
sw.writeNamespace("url1", "S");
sw.writeStartElement("url1", "Envelope");
sw.writeStartElement("url1", "Body");
sw.writeStartElement("url2", "name1");
sw.writeNamespace("url2", "ns2");
but the result is not what I expect:
<S:Envelope xmlns:S="url1">
<S:Body>
<n1:name1 xmlns:n1="url2">
..
As specified in the documentation, the defaut prefix n1 is used instead of ns2.
If I swap the last 2 lines, I have:
<S:Envelope xmlns:S="url1">
<S:Body xmlns:ns2="url2">
<ns2:name1>
..
What am I doing wrong?

From the documentation of QXmlStreamWriter:
you can bypass the stream writer's namespace support and use overloaded methods that take a qualified name instead.
That is, use this overloaded method: QXmlStreamWriter::writeStartElement.
I suggest you to modify your code like that:
sw.writeNamespace("url1", "S");
sw.writeStartElement("url1", "Envelope");
sw.writeStartElement("url1", "Body");
sw.writeStartElement("ns2:name1");
sw.writeNamespace("url2", "ns2");
This produces the result you have stated as desired:
<S:Envelope xmlns:S="url1">
<S:Body>
<ns2:name1 xmlns:ns2="url2">

Related

Using ColdFusion to parse WSDL/SOAP

I'm passing in a USERID into what I believe to be either a SOAP request. I've never worked with SOAP and not quite sure where to start so if my question doesn't quite make sense, let me know and I'll try to fill in missing details.
Context for question:
I'm converting an excel macro (the macro will query pass the userid to the server and the server will return employee details such as name, email, address, etc.) and turning into a web lookup.
I have the following code:
<cfscript>
variables.sso = '55555';
variables.serverURL = "http://search.corporate.ge.com/ldq/Query";
variables.queryString = "?serverID=ssoprod&searchBase=ou=domainWorker,+o=domain.com&Prebuilt=true&scope=2&filter=(domainoraclehrid=#variables.sso#)";
variables.webservice = "#variables.serverURL##variables.queryString#";
</cfscript>
<cfdump var="#variables.webservice#">
<cfhttp url="#variables.webservice#" method="get" result="response"></cfhttp>
<cfdump var="#response.fileContent#" label="soap content">
When I take the value from the dump of variables.webservice, and paste it directly into a browser, I get the following (assume userID of 55555):
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dsml="http://www.dsml.org/DSML">
<SOAP-ENV:Body>
<dsml:dsml xmlns:dsml="http://www.dsml.org/DSML">
<dsml:directory-entries>
<dsml:entry dn="domainssouid=1D8B0D04-91F0-1CAE-9BD7-002128B20D70,ou=domainWorker, o=domain.com">
...
<dsml:attr name="employeetype">
<dsml:value>Contractor</dsml:value>
</dsml:attr>
<dsml:attr name="givenname">
<dsml:value>John</dsml:value>
</dsml:attr>
<dsml:attr name="postalcode">
<dsml:value>90210</dsml:value>
</dsml:attr>
<dsml:attr name="domainoraclehrid">
<dsml:value>456456987</dsml:value>
</dsml:attr>
<dsml:attr name="mail">
<dsml:value>John.Doe#domain.com</dsml:value>
</dsml:attr>
<dsml:attr name="cn">
<dsml:value>Doe, John</dsml:value>
</dsml:attr>
...
</dsml:entry>
</dsml:directory-entries>
</dsml:dsml>
</SOAP-ENV:Body>
BUT when I dump of #response.fileContent#, I get something different, I get:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:f="http://www.w3.org/2001/06/soap-faults">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<SOAP-ENV:faultcode>MustUnderstand</SOAP-ENV:faultcode>
<SOAP-ENV:faultstring>ou=domainWorker,+o=domain.com: [LDAP: error code 34 - Invalid DN], Name Not valid - ou=domainWorker,+o=domain.com - filter -(domainoraclehrid=55555) </SOAP-ENV:faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Ultimately what I'd like to do is parse out the "cn", and "mail" details. What am I missing here? I suspect it might have something to do with ME accessing the URL directly (logged in user) where as the request is being made from a server and isn't "authenticated". If that is the case, how can I resolve this?
Update:
(Originally, I was a little perplexed about why I couldn't reproduce your results with CF2018, but now that I know you're using Lucee, the difference makes sense.)
Looking over the successful response, I noticed it doesn't contain a +, before the "o" (organization Name):
<dsml:entry dn="domainssouid=xxxx,ou=domainWorker, o=domain.com">
Which means the VBA call is treating the + as an encoding for a space, but CFHTTP is encoding it as a literal plus sign instead, causing an error because it breaks the LDAP query:
[LDAP: error code 34 - Invalid DN], Name Not valid - ou=domainWorker,+o=domain.com
The solution is to get rid of the plus sign + and replace it with a space:
searchBase=ou=domainWorker, o=domain.com
Interestingly, dumping the http request data shows that apparently CF2018 does things differently. Unlike Lucee, CF2018 treats the plus sign as a space.
CF2018 => %20 (space)
searchBase=ou%3DdomainWorker%2C%20o%3Ddomain.com
Lucee 5.2.8.50 => %2B (plus sign)
searchBase=ou%3DdomainWorker%2C%2Bo%3Ddomain.com

Apache CXF client namespace is in element instead of envelope

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".

Xpath matching expression gives two strings values

I have an requirement to extract below value.
<cidx:ReferenceInformation ReferenceType="DeliveryNoteNumber">
<cidx:DocumentReference>
<cidx:DocumentIdentifier>5004330471</cidx:DocumentIdentifier>
</cidx:DocumentReference>
</cidx:ReferenceInformation>
I am using the below Xpath expression and getting 2 values match.
Expression
/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='ShipNotice']/*[local-name()='ShipNoticeBody']/*[local-name()='ShipNoticeProperties']/*[local-name()='ReferenceInformation']/*[local-name()='DocumentReference']/*[local-name()='DocumentIdentifier']/text()
Output
<?xml version="1.0" encoding="UTF-8"?>
<result>
5004330471
0803692106
</result>
I have tested the Xpath in the online tool
[http://www.xpathtester.com/xpath][1]
Below is INPUT XML
<SOAP:Envelope xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP:Header/>
<SOAP:Body>
<cidx:ShipNotice xmlns:cidx="urn:cidx:names:specification:ces:schema:all:4:0" Version="4.0">
<cidx:Header>
<cidx:ThisDocumentIdentifier>
<cidx:DocumentIdentifier>0000001113658104</cidx:DocumentIdentifier>
</cidx:ThisDocumentIdentifier>
<cidx:ThisDocumentDateTime>
<cidx:DateTime DateTimeQualifier="On">2015-01-22T20:15:35Z</cidx:DateTime>
</cidx:ThisDocumentDateTime>
<cidx:From>
<cidx:PartnerInformation>
<cidx:PartnerName>MOS Company</cidx:PartnerName>
<cidx:PartnerIdentifier Agency="AGIIS-EBID">1234567890123</cidx:PartnerIdentifier>
<cidx:AddressInformation>
<cidx:AddressLine>N. Lindbergh</cidx:AddressLine>
<cidx:CityName>Columbia</cidx:CityName>
<cidx:StateOrProvince>MO</cidx:StateOrProvince>
<cidx:PostalCode>63190</cidx:PostalCode>
<cidx:PostalCountry>US</cidx:PostalCountry>
</cidx:AddressInformation>
</cidx:PartnerInformation>
</cidx:From>
<cidx:To>
<cidx:PartnerInformation>
<cidx:PartnerName> DIV BOWLNG GR VERA</cidx:PartnerName>
<cidx:PartnerIdentifier Agency="AssignedByPapiNet">0001664057</cidx:PartnerIdentifier>
<cidx:ContactInformation>
<cidx:ContactName>2015</cidx:ContactName>
<cidx:ContactDescription>SeedYear</cidx:ContactDescription>
</cidx:ContactInformation>
<cidx:ContactInformation>
<cidx:ContactName>1024122440000</cidx:ContactName>
<cidx:ContactDescription>AGIIS-EBID</cidx:ContactDescription>
</cidx:ContactInformation>
<cidx:AddressInformation>
<cidx:AddressLine>17410 PIKE 291</cidx:AddressLine>
<cidx:CityName>BOWLING GREEN</cidx:CityName>
<cidx:StateOrProvince>MO</cidx:StateOrProvince>
<cidx:PostalCode>633343045</cidx:PostalCode>
<cidx:PostalCountry>US</cidx:PostalCountry>
</cidx:AddressInformation>
</cidx:PartnerInformation>
</cidx:To>
</cidx:Header>
<cidx:ShipNoticeBody>
<cidx:ShipNoticeProperties>
<cidx:ShipmentIdentification>
<cidx:DocumentReference>
<cidx:DocumentIdentifier>0803692106</cidx:DocumentIdentifier>
</cidx:DocumentReference>
</cidx:ShipmentIdentification>
<cidx:ShipDate>
<cidx:DateTime DateTimeQualifier="On">2015-01-22T00:00:00Z</cidx:DateTime>
</cidx:ShipDate>
<cidx:PurchaseOrderInformation>
<cidx:DocumentReference>
<cidx:DocumentIdentifier>PO8956234</cidx:DocumentIdentifier>
<cidx:ReferenceItem>000530</cidx:ReferenceItem>
</cidx:DocumentReference>
</cidx:PurchaseOrderInformation>
<cidx:TransportMethodCode Domain="UN-Rec-19">3</cidx:TransportMethodCode>
<cidx:ReferenceInformation ReferenceType="DeliveryNoteNumber">
<cidx:DocumentReference>
<cidx:DocumentIdentifier>5004330471</cidx:DocumentIdentifier>
</cidx:DocumentReference>
</cidx:ReferenceInformation>
<cidx:ReferenceInformation ReferenceType="BillOfLadingNumber">
<cidx:DocumentReference>
<cidx:DocumentIdentifier>0803692106</cidx:DocumentIdentifier>
</cidx:DocumentReference>
</cidx:ReferenceInformation>
<cidx:ShipNoticeDate>
<cidx:DateTime DateTimeQualifier="On">2015-01-22T20:15:35Z</cidx:DateTime>
</cidx:ShipNoticeDate>
</cidx:ShipNoticeProperties>
</cidx:ShipNoticeBody>
</cidx:ShipNotice>
</SOAP:Body>
</SOAP:Envelope>
Can anyone please advise how to get only this value?
<cidx:DocumentIdentifier>5004330471</cidx:DocumentIdentifier>
Looks like you need to test the ReferenceType attribute in the cidx:ReferenceInformation element:
/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='ShipNotice']/*[local-name()='ShipNoticeBody']/*[local-name()='ShipNoticeProperties']/*[local-name()='ReferenceInformation' and #ReferenceType='DeliveryNoteNumber']/*[local-name()='DocumentReference']/*[local-name()='DocumentIdentifier']
Note: This xpath can be cleaned up a lot if you can declare the namespaces and use prefixes in the path.
You could also clean it up using * for the prefix if you're using XPath 2.0...
/*:Envelope/*:Body/*:ShipNotice/*:ShipNoticeBody/*:ShipNoticeProperties/*:ReferenceInformation[#ReferenceType='DeliveryNoteNumber']/*:DocumentReference/*:DocumentIdentifier

How does one bind namespace prefixes when using QXmlQuery (Qt XQuery)?

I'm attempting to use QXmlQuery to execute an XQuery expression against a document with a declared default namespace.
For discussion:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://namespace.com/ns1">
<node1 attr1="hi"/>
</root>
Now, I have the following to open and query against the document:
QFile temp("my.xml");
temp.open(QIODevice::ReadOnly | QIODevice::Text);
QXmlQuery query;
query.setFocus(&temp);
QXmlResultItems items;
query.setQuery("/root");
query.evaluateTo(&items);
In running this, 'items' never has data in it, since the document is namespaced. Of course, if I remove the default namespace declaration, 'items' does have the correct data, but I don't have that luxury.
I've tried changing the query to: "/prefix:root", and Qt barks a warning like:
No namespace binding exists for the prefix prefix in prefix:root
So namespace binding does exist! But where? I see QXmlNamePool, but it has no mutator methods. I can create a QXmlName with the pool from the query ala:
QXmlName name(query.namePool(), "prefix", "http://namespace.com/ns1");
But it doesn't change anything. I'm at a loss, other toolkits I have used have simple methods to bind prefixes to namespace URIs.
I believe if you would change your query to
...
QXmlResultItems items;
query.setQuery("declare default element namespace \"http://namespace.com/ns1\"; /root");
...
it should return the data.
hope this helps, regards

Passing serialized object through web service vs. Passing the object

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.