I'm seeing some unusual behavior when using the DataContractSerializer. I have defined a message contract like so:
namespace MyNamespace.DataContracts
{
[MessageContract(WrapperName = "order", WrapperNamespace = #"http://example.com/v1/order")]
public class MyOrder
{
[MessageBodyMember(Namespace = #"http://example.com/v1/order", Order = 1)]
public MyStore store;
[MessageBodyMember(Namespace = #"http://example.com/v1/order", Order = 2)]
public MyOrderHeader orderHeader;
[MessageBodyMember(Namespace = #"http://example.com/v1/order", Order = 3)]
public List<MyPayment> payments;
[MessageBodyMember(Namespace = #"http://example.com/v1/order", Order = 4)]
public List<MyShipment> shipments;
}
.
.
I'm sending it an XML message that looks like this:
<?xml version="1.0" encoding="utf-8"?>
<order xmlns="http://example.com/v1/order>
<store>
...
</store>
<orderHeader>
...
</orderHeader>
<payments>
<payment>
...
</payment>
</payments>
<shipments>
<shipment>
...
</shipment>
</shipments>
</order>
My service deserializes this XML as expected. Inside my service, I'm using the DataContractSerializer to create an XML string and that's where things get weird. I'm using the serializer like this:
DataContractSerializer serializer = new DataContractSerializer(typeof(MyOrder));
using (MemoryStream ms = new MemoryStream())
{
serializer.WriteObject(ms, order);
ms.Position = 0;
StreamReader sr = new StreamReader(ms);
string outputMessage = sr.ReadToEnd();
}
Once this finishes, the outputMessage contains the following XML:
<?xml version="1.0" encoding="utf-8"?>
<MyOrder xmlns="http://example.com/v1/order" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<order>
<store>
...
</store>
<orderHeader>
...
</orderHeader>
<payments>
<payment>
...
</payment>
</payments>
<shipments>
<shipment>
...
</shipment>
</shipments>
</order>
</MyOrder>
Needless to say, anything expecting to receive the original XML message will fail to parse this. So I guess I have two questions:
Why is the DataContractSerializer
adding the extra outer node to my
XML output?
Is there a way to stop it from doing
this?
Thanks.
I should probably add this is with .NET 4.
You could try using WriteObjectContent instead of WriteObject, but I'm unable to reproduce your problem using the code you supplied. All the extra class defintions that are part of your message contract are empty in my definition, but this is the XML I am getting:
<MyOrder xmlns="http://schemas.datacontract.org/2004/07/SandboxApp"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<orderHeader i:nil="true"/>
<payments i:nil="true"/>
<shipments i:nil="true"/>
<store i:nil="true"/>
</MyOrder>
Which also seems odd, since it seems to ignore the WrapperName. Same result in .NET 3.5 SP1 and .NET 4.0.
Related
I'm trying to integrate the DHL CreateShipmentOrder API.
When I send the document prepared by DHL with the Soap UI program, it works correctly.
My code and the returned result are as follows.
When I contacted DHL, I could not reach the result. Can you help me ?
I'm trying to integrate the DHL CreateShipmentOrder API.
When I send the document prepared by DHL with the Soap UI program, it works correctly.
My code and the returned result are as follows.
When I contacted DHL, I could not reach the result. Can you help me ?
My Code;
public static void Main()
{
// Create Web request to get title elements
var serviceUrl = "https://cig.dhl.de/services/production/soap";
HttpWebResponse response = null;
try
{
String sPayload = String.Format(#"<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:cis='http://dhl.de/webservice/cisbase' xmlns:ns='http://dhl.de/webservices/businesscustomershipping/3.0'>
<soapenv:Header>
<cis:Authentification>
<cis:user>*****</cis:user>
<cis:signature>*******</cis:signature>
</cis:Authentification>
</soapenv:Header>
<soapenv:Body>
<ns:CreateShipmentOrderRequest>
<ns:Version>
<majorRelease>3</majorRelease>
<minorRelease>1</minorRelease>
</ns:Version>
<ShipmentOrder>
<sequenceNumber/>
<Shipment>
<ShipmentDetails>
<product>V01PAK</product>
<cis:accountNumber>********</cis:accountNumber>
<customerReference>Ref. 123456</customerReference>
<shipmentDate>2022-01-06</shipmentDate>
<ShipmentItem>
<weightInKG>5</weightInKG>
<lengthInCM>60</lengthInCM>
<widthInCM>30</widthInCM>
<heightInCM>15</heightInCM>
</ShipmentItem>
<Notification> <recipientEmailAddress>empfaenger#test.de</recipientEmailAddress>
</Notification>
</ShipmentDetails>
<Shipper>
<Name>
<cis:name1>Absender Zeile 1</cis:name1>
<cis:name2>Absender Zeile 2</cis:name2>
<cis:name3>Absender Zeile 3</cis:name3>
</Name>
<Address>
<cis:streetName>Vegesacker Heerstr.111</cis:streetName>
<cis:zip>28757</cis:zip>
<cis:city>Bremen</cis:city>
<cis:Origin>
<cis:country/>
<cis:countryISOCode>DE</cis:countryISOCode>
</cis:Origin>
</Address>
<Communication>
<!--Optional:-->
<cis:phone>+49421987654321</cis:phone>
<cis:email>absender#test.de</cis:email>
<!--Optional:-->
<cis:contactPerson>Kontaktperson Absender</cis:contactPerson>
</Communication>
</Shipper>
<Receiver>
<cis:name1>Empfänger Zeile 1</cis:name1>
<Address>
<cis:name2>Empfänger Zeile 2</cis:name2>
<cis:name3>Empfänger Zeile 3</cis:name3>
<cis:streetName>An der Weide 50a</cis:streetName>
<cis:zip>28195</cis:zip>
<cis:city>Bremen</cis:city>
<cis:Origin>
<cis:country/>
<cis:countryISOCode>DE</cis:countryISOCode>
</cis:Origin>
</Address>
<Communication>
<cis:phone>+49421123456789</cis:phone>
<cis:email>empfaenger#test.de</cis:email>
<cis:contactPerson>Kontaktperson Empfänger</cis:contactPerson>
</Communication>
</Receiver>
</Shipment>
<PrintOnlyIfCodeable active='1'/>
</ShipmentOrder>
<labelResponseType>URL</labelResponseType>
<combinedPrinting>0</combinedPrinting>
</ns:CreateShipmentOrderRequest>
</soapenv:Body>
</soapenv:Envelope>");
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(serviceUrl) as HttpWebRequest;
webRequest.ContentType = "text/xml;charset=utf-8";
webRequest.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes("******:*****"));
webRequest.Method = "POST";
using (var sw = new StreamWriter(webRequest.GetRequestStream()))
{
sw.Write(sPayload);
sw.Close();
}
response = (HttpWebResponse) webRequest.GetResponse();
}
catch (WebException ex)
{
Console.WriteLine("Exception: " + ex.Message);
}
finally
{
if (response != null)
Console.WriteLine("Http status = " + response.StatusCode + " - " + response.StatusDescription);
}
if( response != null && response.StatusDescription == "OK" ) {
Console.WriteLine("Successfull");
}
Console.WriteLine(DateTime.Now.ToString());
}
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Client</faultcode>
<faultstring>SECURITY_VIOLATION</faultstring>
</soap:Fault>
</soap:Body>
</soap:Envelope>
In order to communicate with DHL, it is imperative to use the DHL WSDL files and accesses. (Download at https://developer.dhl.de/)
Benjamin Müller gives the answer to your question under the following link. It is best to use the latest WSDL from DHL, a few objects (actually only names) still have to be adjusted.
Java WSDL DHL Classes
The two lines below the following line must be used, otherwise the authentication will not work.
// overwrite BasicAuth Username and Password
This is the only way DHL will not reject the request with "SECURITY_VIOLATION".
All other ways are not approved by DHL and lead to an error message.
Using response As WebResponse = request.GetResponse()
Using rd As New StreamReader(response.GetResponseStream())
Dim soapResult As String = rd.ReadToEnd()
SerializeCollection(soapResult)
the soapResult generate the following :
<?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">
<SOAP-ENV:Body>
<createShipmentResponse xmlns="http://www.royalmailgroup.com/api/ship/V2">
<integrationHeader>
<dateTime xmlns="http://www.royalmailgroup.com/integration/core/V1">2016-03-23T06:55:32</dateTime>
<version xmlns="http://www.royalmailgroup.com/integration/core/V1">2</version>
<identification xmlns="http://www.royalmailgroup.com/integration/core/V1">
<applicationId>RMG-API-G-01</applicationId>
<transactionId>730222611</transactionId>
</identification>
</integrationHeader>
<integrationFooter>
<errors xmlns="http://www.royalmailgroup.com/integration/core/V1">
<error>
<errorCode>E1105</errorCode>
<errorDescription>The countryCode specified is not valid</errorDescription>
</error>
<error>
<errorCode>E1001</errorCode>
<errorDescription>Postcode AA9 0AA invalid</errorDescription>
</error>
</errors>
<warnings xmlns="http://www.royalmailgroup.com/integration/core/V1">
<warning>
<warningCode>W0036</warningCode>
<warningDescription>E-mail option not selected so e-mail address will be ignored</warningDescription>
</warning>
<warning>
<warningCode>W0035</warningCode>
<warningDescription>SMS option not selected so Telephone Number will be ignored</warningDescription>
</warning>
</warnings>
</integrationFooter>
</createShipmentResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
i am using this method to serialize this response :
Private Sub SerializeCollection(filename As String)
Dim Emps As createShipmentResponse = New createShipmentResponse()
' Note that only the collection is serialized -- not the
' CollectionName or any other public property of the class.
Dim x As XmlSerializer = New XmlSerializer(GetType(createShipmentResponse))
Dim writer As TextWriter = New StreamWriter(filename)
x.Serialize(writer, Emps)
writer.Close()
End Sub
but i am having this error : Illegal characters in path
what does that mean ? and how can i fix this ?
Your SerializeCollection() method expects a file path to serialize to a file. You're currently passing the contents of your response which is why it doesn't work.
If you haven't written the method yourself I think you've not found completely what you're looking for.
This is an example of how the method should be called:
SerializeCollection("C:\Users\Vincent\Desktop\Hello.bin") 'The extension doesn't need to be '.bin', but it's an example.
The method has currently no way of getting your response, for that you should add a second parameter.
As I'm not familiar with Soap serialization I'm afraid I cannot help you further.
I need to be able to extract individual nodes from this file into variables for further manipulation. I'm writing to the console to see what information is being pulled, but I am struggling to pull the name or description.
I can successfully print the entire file. I've tried getting individual nodes using placemark.<name>.Value and placemark.Element("name").Value, the second of which throws a NullReferenceException. Any ideas on how to be able to pull out the name and description in this instance?
Imports System.Xml
Imports System.Xml.Linq 'Visual Studio 2015 tells me this isn't needed
Imports System.Core 'Visual Studio 2015 tells me this isn't needed
Dim file As XDocument = XDocument.Load(filePath)
Dim placemarks As IEnumerable(Of XElement) = From test In file.Root.Elements()
For Each placemark As XElement In placemarks
Console.WriteLine(placemark) 'This works
Console.WriteLine(placemark.<name>.Value) 'This prints an empty line
Console.WriteLine(placemark.Element("description").Value) 'This throws a NullReferenceException
Next
This is the structure
<?xml version='1.0' encoding='UTF-8'?>
<kml xmlns='http://www.opengis.net/kml/2.2'>
<Document>
<name>Untitled layer</name>
<Placemark>
<name>Name 1</name>
<description>Description 1</description>
<ExtendedData>
<Data name='Test data one'>
<value>Test data 1</value>
</Data>
</ExtendedData>
<Point>
<coordinates>34725567547</coordinates>
</Point>
</Placemark>
<Placemark>
<name>Name 2</name>
<description>Description 2</description>
<ExtendedData>
<Data name='Test data two'>
<value>Test data 2</value>
</Data>
</ExtendedData>
<Point>
<coordinates>056795763767</coordinates>
</Point>
</Placemark>
If I have understood you correctly, you are trying to fetch the name & description present inside the PlaceMark node. But, since you are only fetching Root.Elements() your query will only fetch the complete XML starting from your root node.
You need to find the Descendants of PlaceMark node because you need to fetch the name & description inside it. Also, since the root node kml consists of namespace you need to specify that as well.
Here is the code:-
Dim ns As XNamespace = "http://www.opengis.net/kml/2.2"
Dim placeMarks = From test In file.Root.Element(ns + "Document")
.Descendants(ns + "Placemark") Select test
For Each pm In placeMarks
Console.WriteLine("Name: {0}", pm.Element(ns + "name").Value)
Console.WriteLine("Description: {0}", pm.Element(ns + "description").Value)
Console.WriteLine()
Next
I am getting following output:-
My XML
<?xml version="1.0" encoding="utf-8"?>
<metadata created="2014-05-15T12:26:07.701Z" xmlns="http://site/cu-2.0#" xmlns:ext="http://site/cu/b-2.0">
<customer-list count="47" offset="0">
<customer id="7123456" type="Cust" ext:mark="1">
<name>Tony Watt</name>
<sort-name>Watt, Tony</sort-name>
<gender>male</gender>
<country>US</country>
<knownAs-list>
<knownAs locale="ko" sort-name="Tony Watt"</knownAs>
<knownAs locale="ja" sort-name="Watt Tony"</knownAs>
</knownAs-list>
<tag-list>
<begin>Country</begin>
<tag count="1">
<name>usa</name>
</tag-list>
</customer>
<customer id="9876543" type="Cust" ext:mark="2">
....
</customer-list>
So i have some code that gets all the data. I went one step further to use Anonymous types and add the values into a class as below
Dim c = From cust As XElement In XDoc.Descendants(ns + "customer")
Select New Customer() With {.Name = cust.Element(ns + "name"),
.Surname = CStr(cust.Element(ns + "surname")),
.Id = cust.Attribute("id"),
.Tag = CStr(cust.Element("tag-list").Element("begin"))}
The above code returns data from the XML, but adding this line of code
.Tag = CStr(cust.Element("tag-list").Element("begin"))
throws an exception, "Object reference not set to an instance of an object". Now theres two possibilities here
I have my code wrong for that particular line (to retrieve 'begin' from the 'tag-list' element)
I know some tag-list elements dont have a nested begin element so that could be adding some confusion. I added Cstr to overcome this but not sure if this is enough?
After reading MSDN it seems using .Descendants (Xdoc.Descendants) would get the all the data from all elements where Elements would return data upto the path i have stated, so as far as i can tell the data 'should' be available with the above code. Could anyone assist me in getting the begin data from tag-list?
The XML namespace declaration is missing. Use
.Tag = CStr(cust.Element(ns + "tag-list").Element(ns + "begin"))
So, for the Intuit IPP Rest API,
Say if i want to query a customer who's name is ABC, i can use a http get request like this
https://qb.sbfinance.intuit.com/v3/company/198445012/query?query=select Id from Customer where FullyQualifiedName%3D'ABC'
&3D is a url escape of '=', this works without any problem.
Now if the customer's name is A&B, I tried the query string like this
select Id from Customer where FullyQualifiedName='A&B'
After the url encoding, it looks like this
https://qb.sbfinance.intuit.com/v3/company/198445012/query?query=select Id from Customer where FullyQualifiedName%3D'A%26B'
It will fail.
Any Idea?
Update
The above urls i copied from the IPP's API explorer.
Here is the code, I am using DevDefined.OAuth
IConsumerRequest conReq = _oSession.Request();
conReq = conReq.Get();
conReq.AcceptsType = "application/xml";
//conReq = conReq.ForUrl(string.Format(#"https://qb.sbfinance.intuit.com/v3/company/{0}/query?query={1}", Settings.Default.QuickBooksOnlineRealmId, #"select * from Customer where DisplayName='ABC'")); if use this line, it works fine
conReq = conReq.ForUrl(string.Format(#"https://qb.sbfinance.intuit.com/v3/company/{0}/query?query={1}", Settings.Default.QuickBooksOnlineRealmId, #"select * from Customer where DisplayName='A&B'"));
try
{
string str = conReq.ReadBody();
catch (Exception ex)
{
//ex.Message
}
the returned xml data like this
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<IntuitResponse xmlns="http://schema.intuit.com/finance/v3" time="2014-03-20T06:24:12.408-07:00">
<Fault type="ValidationFault">
<Error code="4000">
<Message>Error parsing query</Message>
<Detail>QueryParserError: Invalid content. Lexical error at line 1, column 44. Encountered: <EOF> after : "\'A"</Detail>
</Error>
</Fault>
</IntuitResponse>
I am not 100% sure, yesterday when i test, it actually return something says the oauth failed. But this is what I got this morning.
Actually, you can try it within IPP's API explorer, it gives the same result.
The devdefined's code for ForUrl
public static IConsumerRequest ForUrl(this IConsumerRequest request, string url)
{
request.Context.RawUri = new Uri(url);
return request;
}
That will encode the url as
https://qb.sbfinance.intuit.com/v3/company/1122502005/query?query=select%20*%20from%20Customer%20where%20DisplayName='A&B'
Ok, finally, found the issue:
The real issue is Uri(url) won't escape the & in 'A&B' because it doesn't know if it is a url & or part of the data, So i changed the following line
conReq = conReq.ForUrl(string.Format(#"https://qb.sbfinance.intuit.com/v3/company/{0}/query?query={1}", Settings.Default.QuickBooksOnlineRealmId, #"select * from Customer where DisplayName='A&B'"));
as
conReq = conReq.ForUrl(string.Format(#"https://qb.sbfinance.intuit.com/v3/company/{0}/query?query={1}", Settings.Default.QuickBooksOnlineRealmId, Uri.EscapeDataString(#"select * from Customer where DisplayName='A&B'")));
use Uri.EscapeDataString to escape the data query string first.
If you create a customer with name 'A&B', then V3 service returns that customer object like below -
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<IntuitResponse xmlns="http://schema.intuit.com/finance/v3" time="2014-03-20T01:54:46.834-07:00">
<Customer domain="QBO" sparse="false">
<Id>10</Id>
<SyncToken>0</SyncToken>
<MetaData>
<CreateTime>2014-03-20T01:54:46-07:00</CreateTime>
<LastUpdatedTime>2014-03-20T01:54:47-07:00</LastUpdatedTime>
</MetaData>
<FullyQualifiedName>A&B</FullyQualifiedName>
<DisplayName>A&B</DisplayName>
<PrintOnCheckName>A&B</PrintOnCheckName>
<Active>true</Active>
<Taxable>true</Taxable>
<Job>false</Job>
<BillWithParent>false</BillWithParent>
<Balance>100.00</Balance>
<BalanceWithJobs>100.00</BalanceWithJobs>
<PreferredDeliveryMethod>Email</PreferredDeliveryMethod>
</Customer>
</IntuitResponse>
To retrieve this object by name, you need to use the following query
SELECT * FROM Customer WHERE DisplayName = 'A&B'
But it needs to be url encoded like following
SELECT+*+FROM+Customer+WHERE+DisplayName+%3D+%27A%26B%27
Java code to achieve this -
Customer customer = GenerateQuery.createQueryEntity(Customer.class);
String query = select($(customer)).where($(customer.getDisplayName()).eq("A&B")).generate();
// Query output - SELECT * FROM Customer WHERE DisplayName = 'A&B'
String encodedUrl = URLEncoder.encode(query, "UTF-8");
It works perfectly. (Devkit handles all these pretty well )
Hope this answers your qts.
Thanks
Please see an example here to escape special chars-
using Intuit.Ipp.Core;
using Intuit.Ipp.Data;
using Intuit.Ipp.LinqExtender;
using Intuit.Ipp.QueryFilter;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
static class SampleCalls
{
public static Customer QueryCustomerByDisplayName(ServiceContext context, string displayName) {
displayName = displayName.Replace("'", "\\'"); //Escape special characters
QueryService<Customer> customerQueryService = new QueryService<Customer>(context);
return customerQueryService.Where(m => m.DisplayName == displayName).FirstOrDefault();
}
}