I'm attempting to Create a Job and Add a Batch using the Salesforce Bulk API, but I receive an XML Parsing Error back from Salesforce.
As advised in the Connecting to SalesForce bulk API using C# question, I'm using the partner WSDL to perform the login; that and the Create Job call are working. (Before I had attempted to perform the login by POSTing XML as the documentation had mentioned; I couldn't get that to work, either.)
Unfortunately, I'm a bit rusty with C#, but here is the code I'm working with.
...
using SalesforceTest.sforce;
...
namespace SalesforceTest
{
public partial class InvokeBulkAPI : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e) { }
public string SalesforceLogin(string username, string password)
{
SforceService binding = new SforceService();
binding.Timeout = 60000;
try
{
LoginResult lr = binding.login(username, password);
binding.Url = lr.serverUrl;
return lr.sessionId;
}
catch (SoapException e) { return e.Message; }
}
public string CreateJob(string sfSessionId, string sfOperation, string sfObjectName)
{
string str = "";
string reqURL = "";
byte[] bytes;
XmlDocument reqDoc;
XmlDocument respDoc;
str = ""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" // added \r\n as recommended by L.B's answer
+ "<jobInfo xmlns=\"http://www.force.com/2009/06/asyncapi/dataload\">"
+ " <operation></operation>" // removed "+sfOperation+"
+ " <object></object>" // removed "+sfObjectName+"
+ " <contentType>XML</contentType>" // should be CSV, NOT XML
+ "</jobInfo>"
;
reqURL = "https://cs12-api.salesforce.com/services/async/23.0/job";
reqDoc = new XmlDocument();
reqDoc.LoadXml(str);
// added XML modifications
reqDoc.GetElementsByTagName("operation")[0].InnerText = sfOperation;
reqDoc.GetElementsByTagName("object")[0].InnerText = sfObjectName;
bytes = System.Text.Encoding.ASCII.GetBytes(reqDoc.InnerXml);
respDoc = Post(bytes, reqURL, sfSessionId); // create job
string JobId = (respDoc != null) ?
(respDoc.GetElementsByTagName("id").Count > 0) ?
(respDoc.GetElementsByTagName("id")[0].InnerText) :
"" :
""
;
return JobId;
}
public void AddBatch(string sfSessionId, string sfJobId, byte[] fileBytes)
{
string reqURL = "https://cs12-api.salesforce.com/services/async/23.0/job/" + sfJobId + "/batch";
XmlDocument respDoc = Post(fileBytes, reqURL, sfSessionId);
}
public XmlDocument Post(byte[] bytes, string reqURL, string sfSessionId)
{
WebRequest req = WebRequest.Create(reqURL);
req.Method = "POST";
req.ContentLength = bytes.Length;
req.ContentType = "application/xml; charset=UTF-8"; // should be text/csv; when passing a CSV file
req.Headers.Add("X-SFDC-Session: " + sfSessionId);
System.IO.Stream strm = req.GetRequestStream();
strm.Write(bytes, 0, bytes.Length);
strm.Close();
WebResponse resp = req.GetResponse();
System.IO.Stream respStrm = resp.GetResponseStream();
XmlDocument respDoc = new XmlDocument();
respDoc.Load(respStrm);
return respDoc;
}
protected void Button1_Click(object sender, EventArgs e)
{
string SessionId = SalesforceLogin(this.TextBox1.Text, this.TextBox2.Text);
string JobId = CreateJob(SessionId, "insert", "Contact");
if (JobId.Length > 0)
{
AddBatch(SessionId, JobId, this.FileUpload1.FileBytes);
}
}
}
}
The file I'm posting is the same as the example in the documentation.
FirstName,LastName,Department,Birthdate,Description
Tom,Jones,Marketing,1940-06-07Z,"Self-described as ""the top"" branding guru on the West Coast"
Ian,Dury,R&D,,"World-renowned expert in fuzzy logic design.
Influential in technology purchases."
My question is why would I receive an XML Parsing Error from Salesforce on Line Number 1, Column 1?
This is what I receive:
XML Parsing Error: syntax error
Location: https://####.salesforce.com/services/async/23.0/job/###/batch/###/request
Line Number 1, Column 1:
FirstName,LastName,Department,Birthdate,Description
^
Any help would be appreciated. Thanks!
First of all never create an xml with string manipulations. Use an XML parser like XmlDocument or XDocument.
Xml Declaration should be on a seperate line <?xml version=\"1.0\" encoding=\"UTF-8\"?>. you forget \r\n
Third, you should set text/csv as content type when sending csv
The example uses curl to send csv. You are sending csv to a service expecting xml. Find an example with xml data or try invoking with same params as they use for curl.
Related
I have a Huawei b525-23a router. Using it's web interface you can send/check SMS but I want to do it automatically from an C# app. I didn't found any API documentation for it so any link will be very good.
I managed to find some HTTPRequests using Chrome but when I use it from C# I get the 125003 error that is according to some google search an authentication problem.
Here are some parts of my code :
private void button4_Click(object sender, EventArgs e)
{
// getting SenInfo and TokInfo
string urlTokenInfo = "http://192.168.8.1/api/webserver/SesTokInfo";
HttpWebRequest requestTokenInfo = (HttpWebRequest)WebRequest.Create(urlTokenInfo);
requestTokenInfo.Method = "GET";
WebResponse responseTokenInfo = requestTokenInfo.GetResponse();
Stream responseTokenInfoStream = responseTokenInfo.GetResponseStream();
string responseTokenInfoString = new StreamReader(responseTokenInfoStream).ReadToEnd();
var rootElement = XElement.Parse(responseTokenInfoString);
string sessionId = rootElement.Element("SesInfo").Value;
string tokenInfo = rootElement.Element("TokInfo").Value;
//_________________________________________________________________________________
// trying to log
String urlLogin = "http://192.168.8.1/api/user/login";
HttpWebRequest requestLogin = (HttpWebRequest)WebRequest.Create(urlLogin);
requestLogin.Method = "POST";
String XMLLogin;
String base64Passwd = Base64Encode(passwd); //function for base64 encode
XMLLogin = " <request ><Username> " + userName + " </Username><Password> " + base64Passwd + " </Password></request> ";
byte[] requestInFormOfBytes = System.Text.Encoding.ASCII.GetBytes(XMLLogin);
requestLogin.ContentType = "text/xml;charset=utf-8";
requestLogin.ContentLength = requestInFormOfBytes.Length;
Stream requestStream = requestLogin.GetRequestStream();
requestStream.Write(requestInFormOfBytes, 0, requestInFormOfBytes.Length);
requestLogin.Headers.Add("__RequestVerificationToken", tokenInfo);
requestLogin.Headers.Add("Cookie", sessionId);
WebResponse raspuns = (HttpWebResponse)requestLogin.GetResponse();
Stream responseStreamLogin = raspuns.GetResponseStream();
string responseStrlogin = new StreamReader(responseStreamLogin).ReadToEnd();
}
}
The response that I get is
<?xml version="1.0" encoding="UTF-8"?><error><message></message><code>125003</code></error>
Thank you for your time reading this and any response will be apreciated.
Mihai Stanciu
125003 error means token verification failed.
Check the session and token values in the first html resource request file
I am completely new to this kind of programming so I don't really know if there is an answer to this already, but I weren't able to find it. So I am testing to see if I can get a dry-run gcm message to work without errors.
The error I get is the error 400 Invalid Request, and it's saying something about the json being invalid, so I have assumed the problem has to do with string manipulation or the definition of postdata, but I can't figure it out. Most of the code is just copy pasted anyway so one could believe that others in a similar situation will get the same error, if they copy from the same source.
And also I have put in actual values for the "lorem"s.
This is the only code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Web.Script.Serialization;
namespace ServerGMC
{
public class ServerGMC
{
static void Main ()
{
// Prepares and calls the function to send message
List<string> RedIdList = new List<string>(1) { "aaaaaaaaaaaaaaaaaaaaaaaa" };
RedIdList.TrimExcess();
Console.WriteLine(SendNotification(RedIdList, "HelloWorld", "test", 220299));
Console.Read();
}
static public string SendNotification(List<string> deviceRegIds, string message, string title, long id)
{
try
{
string regIds = string.Join("\",\"", deviceRegIds);
string AppId = "lorem";
var SenderId = "lorem";
NotificationMessage nm = new NotificationMessage();
nm.Title = title;
nm.Message = message;
nm.ItemId = id;
var value = new JavaScriptSerializer().Serialize(nm);
WebRequest wRequest;
wRequest = WebRequest.Create("https://android.googleapis.com/gcm/send");
wRequest.Method = "post";
wRequest.ContentType = " application/json;charset=UTF-8";
wRequest.Headers.Add(string.Format("Authorization: key={0}", AppId));
wRequest.Headers.Add(string.Format("Sender: id={0}", SenderId));
string postData = "{\"collapse_key\":\"standard\",\"time_to_live\":108,\"delay_while_idle\":true,\"dry_run\":true,\"data\": { \"message\" : " + "\"" + value + "\",\"time\": " + "\"" + System.DateTime.Now.ToString() + "\"},\"registration_ids\":[\"" + regIds + "\"]}";
//string postData = "collapse_key=score_update&time_to_live=108&delay_while_idle=1&data.message=" + value + "&date.time=" + System.DateTime.Now.ToString() + "®istration_ids=" + regIds + "";
Console.WriteLine(postData);
Byte[] bytes = Encoding.UTF8.GetBytes(postData);
wRequest.ContentLength = bytes.Length;
Stream stream = wRequest.GetRequestStream();
stream.Write(bytes, 0, bytes.Length);
stream.Close();
WebResponse wResponse = wRequest.GetResponse();
stream = wResponse.GetResponseStream();
StreamReader reader = new StreamReader(stream);
String response = reader.ReadToEnd();
HttpWebResponse httpResponse = (HttpWebResponse)wResponse;
string status = httpResponse.StatusCode.ToString();
reader.Close();
stream.Close();
wResponse.Close();
if (status == "")
{
return response;
}
else
{
return "";
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
Console.WriteLine();
return "";
}
}
private class NotificationMessage
{
public string Title;
public string Message;
public long ItemId;
}
}
}
The postData isn't properly formatted in JSON. If you check it out using an online formatting tool, it looks like this
{
"collapse_key":"standard",
"time_to_live":108,
"delay_while_idle":true,
"dry_run":true,
"data":{
"message":"{"Title":"test",
"Message":"HelloWorld",
"ItemId":220299}",
"time":"22/04/2016 13:04:38"
},
"registration_ids":["aaaaaaaaaaaaaaaaaaaaaaaa"]
}
You can either remove the data.message node and place its properties in data, or use a 3rd-party JSON parser or System.Web.Helpers.Json.Decode (which were suggested in this issue)
Hopefully this helps with the issue.
Happy coding!
I am trying to parse the following atom XML feed:
<dealer version="1.12" xmlns:atom="http://www.w3.org/2005/Atom"><atom:link rel="self" href="http://Blah.com/dealers/1234"/><atom:link rel="http://Blah.com/rels/dealer_notification_prefs" href="http://Blah.com/dealers/1234/notification_prefs"/><atom:link rel="http://Blah.com/rels/dealer_systems" href="http://Blah.com/dealers/1234/systems"/><atom:link rel="http://Blah.com/rels/dealer_logo" href="http://Blah.com/dealers/1234/logo"/><pid>1234</pid><name>ABC Heating & Air Conditioning</name><first>X</first><last>X</last><street1>PO Box 321</street1><street2/><city>Orson</city><state>IN</state><country>United States</country><postal>46142</postal><phone>317-555-5555</phone><phoneExt/><url></url><email>someone#noemail.com</email></dealer>
The C# code I am using is:
using (var client = new HttpClient()) // Using block for disposing of HttpClient when finished
{
client.DefaultRequestHeaders.Accept.Clear();
client.BaseAddress = new Uri(_baseUriPart); // Set to core base Uri for whole Api
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("OAuth", _builtParamsString);
// Send HTTP Requests Async
try
{
bool respSuccess = false;
HttpResponseMessage response = await client.GetAsync(_resourceUriPart);
//HttpResponseMessage response = await client.SendAsync(myRequestTest);
response.EnsureSuccessStatusCode(); // Throw Exception if not a success code. // ...}
Stream stream = await response.Content.ReadAsStreamAsync();
var prereader = new StreamReader(stream);
string readContent = prereader.ReadToEnd();
string readOut = string.Empty;
TextReader tr = new StringReader(readContent);
XmlReader reader = XmlReader.Create(tr);
SyndicationFeed feed = SyndicationFeed.Load(reader);
if(null!=feed)
{
foreach(var item in feed.Items)
{
//readOut = readOut + item.Title.Text + ":" + ((TextSyndicationContent)item.Content).Text+ Environment.NewLine;
}
}
respSuccess = response.IsSuccessStatusCode;
TextBox1.Text = respSuccess.ToString();
TextBox2.Text = response.StatusCode.ToString();
TextBox3.Text = readOut;
}
catch (HttpRequestException e)
{
TextBox1.Text = "False";
TextBox2.Text = "See Content Message";
TextBox3.Text = e.Message;
}
} // End using block
I can connect to the web service, and request the dealer info as you can see. But the error I get when the SyndicationFeed begins reading the XML is:
"The element with name 'dealer' and namespace '' is not an allowed feed format. "
Can someone please shed some light on this for me? Thanks!!!
dealer isn't a valid tag for the atom feed root. See the Atom Syndication Format RFC for details. It should be atom:feed.
It's unfortunately pretty common to find invalid RSS/Atom feeds. SyndicationFeed is strict so you have to do some massaging of the input data to get it working.
It's ugly but the simple approach is to do a String.Replace for the dealer tags.
// ...
readContent = readContent.Replace("<dealer ", "<atom:feed ").Replace("</dealer>", "</atom:feed>");
TextReader tr = new StringReader(readContent);
// ...
I've also fixed feeds in the past by deriving from XmlTextReader and fixing the bad elements as they are read.
I am using Omniture api to download a report. The report is completed when I checked the status with DataWarehouseCheckRequest method. Now when I try to fetch the report using DataWarehouseGetReportData method, I get
CommunicationException Error in deserializing body of reply message for operation 'DataWarehouseGetReportData
Inner exception says
The specified type was not recognized: name='data_warehouse_report_row', namespace='http://www.omniture.com/', at <rows xmlns=''>
I am new with C# and the API both. Got no idea how to resolve this. Please help.
Thanks
When you want to download a DW report the best option is to do it over http. This the standard way and is much more efficient.
The response to CheckRequest contains a DataURL. Use that to download the data.
Here is some c# sample code I am using for an almost identical API (Partner vs you Enterprise API) (note I'm no c# expert either, so you will need to do a code review on this).
HttpWebResponse statusResponse = null;
string response = "";
StringBuilder sbUrl = new StringBuilder(dwrq.data_url); // hardcode to variable "rest_url" for testing.
HttpWebRequest omniRequest = (HttpWebRequest)WebRequest.Create(sbUrl.ToString());
string timecreated = generateTimestamp();
string nonce = generateNonce();
string digest = getBase64Digest(nonce + timecreated + secret);
nonce = base64Encode(nonce);
omniRequest.Headers.Add("X-WSSE: UsernameToken Username=\"" + username + "\", PasswordDigest=\"" + digest + "\", Nonce=\"" + nonce + "\", Created=\"" + timecreated + "\"");
omniRequest.Method = "GET"; // Switched from POST as GET is the right HTTP verb in this case
try
{
statusResponse = (HttpWebResponse)omniRequest.GetResponse();
using (Stream receiveStream = statusResponse.GetResponseStream())
{
using (StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8))
{
response = readStream.ReadToEnd();
}
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
Console.WriteLine("Response is a TAB delimeted CSV structure. Printing to screen.");
Console.WriteLine(response);
Console.WriteLine("Ending REST...");
Console.WriteLine("Ending ExportRequestSegmentedData...");
and the supporting methods
/*** Here are the private functions ***/
// Encrypting passwords with SHA1 in .NET and Java
// http://authors.aspalliance.com/thycotic/articles/view.aspx?id=2
private static string getBase64Digest(string input)
{
SHA1 sha = new SHA1Managed();
ASCIIEncoding ae = new ASCIIEncoding();
byte[] data = ae.GetBytes(input);
byte[] digest = sha.ComputeHash(data);
return Convert.ToBase64String(digest);
}
// generate random nonce
private static string generateNonce()
{
Random random = new Random();
int len = 24;
string chars = "0123456789abcdef";
string nonce = "";
for (int i = 0; i < len; i++)
{
nonce += chars.Substring(Convert.ToInt32(Math.Floor(random.NextDouble() * chars.Length)), 1);
}
return nonce;
}
// Time stamp in UTC string
private static string generateTimestamp()
{
return DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
}
// C#-Base64 Encoding
// http://www.vbforums.com/showthread.php?t=287324
public static string base64Encode(string data)
{
byte[] encData_byte = new byte[data.Length];
encData_byte = System.Text.Encoding.UTF8.GetBytes(data);
string encodedData = Convert.ToBase64String(encData_byte);
return encodedData;
}
Best of Luck! C.
Before you ask if I've looked at google let me answer yes, I've read page after page. Site after site, and wasn't able to get the information that I needed.
I'm trying to make a very simple update checker for my application. One that will parse the online xml file, and display the data in certain places. As well as being able to parse out the link for the download location(Will not be ftp or anything, but something like a filehost since my hosting plan doesn't allow me to ftp files over 3MBs)
Anyway here is what I got thus far:
XML Code:
<code>
<Info>
<Version>2.8.0.0</Version>
<Link>www.filehost.com</Link>
<Description>Added New Features To GUI</Description>
</Info>
</code>
Here's the application code, and what I want it to show and do.
using System;
using System.Windows.Forms;
using System.Xml;
namespace SAM
{
public partial class UpdateCheck : DevExpress.XtraEditors.XtraForm
{
public UpdateCheck()
{
InitializeComponent();
lblCurrentVersion.Text = "Current Version: " + Application.ProductVersion;
}
private void MainForm_Shown(object sender, EventArgs e)
{
BringToFront();
}
private void BtnChkUpdate_Click(object sender, EventArgs e)
{
XmlDocument doc = new XmlDocument();
doc.Load("http://www.crimson-downloads.com/SAM/UpdateCheck.xml");
}
}
}
I'm looking to have the application parse the xml in this way.
<Version>2.8.0.0</Version> Will change the text for "lblUpdateVersion" like how I got the current version label set in the InitializeComponent();
<Description>Added New Features To GUI</Description> to be parsed out into the "textDescription" Which I can probably do myself.
<Link>www.filehost.com</Link> Will parse into the button control so when pressed will open up the users default browser and follow the link.
I've done the exact thing this in an application of my own.
First, you store an XML file on your webhost that holds the updater information. Mine is at http://getquitter.com/version.xml and is structured as follows:
<versioninformation>
<latestversion>1.2.0.0</latestversion>
<latestversionurl>http://www.getquitter.com/quitter-1.2.0.zip</latestversionurl>
<filename>quitter-1.2.0.zip</filename>
</versioninformation>
Second, write a method to retrieve that xml from your host:
Public Function GetWebPage(ByVal URL As String) As String
Dim Request As System.Net.HttpWebRequest = CType(WebRequest.Create(New Uri(URL)), HttpWebRequest)
With Request
.Method = "GET"
.MaximumAutomaticRedirections = 4
.MaximumResponseHeadersLength = 4
.ContentLength = 0
End With
Dim ReadStream As StreamReader = Nothing
Dim Response As HttpWebResponse = Nothing
Dim ResponseText As String = String.Empty
Try
Response = CType(Request.GetResponse, HttpWebResponse)
Dim ReceiveStream As Stream = Response.GetResponseStream
ReadStream = New StreamReader(ReceiveStream, System.Text.Encoding.UTF8)
ResponseText = ReadStream.ReadToEnd
Response.Close()
ReadStream.Close()
Catch ex As Exception
ResponseText = String.Empty
End Try
Return ResponseText
End Function
Next, call this method to get the xml and load into an xml document.
Dim VersionInfo As New System.Xml.XmlDocument
VersionInfo.LoadXml(GetWebPage("http://www.getquitter.com/version.xml"))
With version.xml loaded, you can now parse out the individual pieces of data you need to determine whether or not you need to grab the new version.
Dim LatestVersion As New Version(QuitterInfoXML.SelectSingleNode("//latestversion").InnerText)
Dim CurrentVersion As Version = My.Application.Info.Version
If LatestVersion > CurrentVersion Then
''download the new version using the Url in the xml
End If
This is how my application does it. You can download the source code if you like (it's an open source application) if you'd like to use it as a model. It's at http://quitter.codeplex.com. Hope this helps!
using System;
using System.Windows.Forms;
using System.Xml;
using System.Net;
using System.IO;
using System.Diagnostics;
namespace SAM
{
public partial class UpdateCheck : DevExpress.XtraEditors.XtraForm
{
public UpdateCheck()
{
InitializeComponent();
lblCurrentVersion.Text = "Current Version: " + Application.ProductVersion;
}
private void MainForm_Shown(object sender, EventArgs e)
{
BringToFront();
}
public static string GetWebPage(string URL)
{
System.Net.HttpWebRequest Request = (HttpWebRequest)(WebRequest.Create(new Uri(URL)));
Request.Method = "GET";
Request.MaximumAutomaticRedirections = 4;
Request.MaximumResponseHeadersLength = 4;
Request.ContentLength = 0;
StreamReader ReadStream = null;
HttpWebResponse Response = null;
string ResponseText = string.Empty;
try
{
Response = (HttpWebResponse)(Request.GetResponse());
Stream ReceiveStream = Response.GetResponseStream();
ReadStream = new StreamReader(ReceiveStream, System.Text.Encoding.UTF8);
ResponseText = ReadStream.ReadToEnd();
Response.Close();
ReadStream.Close();
}
catch (Exception ex)
{
ResponseText = string.Empty;
}
return ResponseText;
}
private void BtnChkUpdate_Click(object sender, EventArgs e)
{
System.Xml.XmlDocument VersionInfo = new System.Xml.XmlDocument();
VersionInfo.LoadXml(GetWebPage("http://www.crimson-downloads.com/SAM/UpdateCheck.xml"));
lblUpdateVersion.Text = "Latest Version: " + (VersionInfo.SelectSingleNode("//latestversion").InnerText);
textDescription.Text = VersionInfo.SelectSingleNode("//description").InnerText;
}
private void simpleButton2_Click(object sender, EventArgs e)
{
Process process = new Process();
// Configure the process using the StartInfo properties.
process.StartInfo.FileName = "http://www.crimson-downloads.com/SAM/Refresh.htm";
process.StartInfo.Arguments = "-n";
process.StartInfo.WindowStyle = ProcessWindowStyle.Maximized;
process.Start();
}
}
}
Short and simple. Thanks man, was having trouble with something else that uses xml, but with the help you gave me I was able to apply the knowledge to that as well and got it working to.