How can I parse SOAP web service in C#? - c#

I'm new in C#,run simple SOAP web service, and that web service return me this:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:getBillsResponse xmlns:ns2="http://obrs/">
<return>
<errorID>7</errorID>
</return>
</ns2:getBillsResponse>
</S:Body>
</S:Envelope>
My code is this for call that web service:
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{
Console.WriteLine("certificate:" + certificate.GetName() + " sslPolicyErrors:" + sslPolicyErrors);
return true;
};
BankWebService.BillingServerService myService = new BankWebService.BillingServerService();
myService.getBill("1","2","3","4","5");
Console.WriteLine("ok");
Console.ReadLine();
I want to write this C# code for getBill output:
if (errorID == 7) then do something...
How can I implement that?

Your proxy code should already have a definition for that if your SOAP is anything to go by.
You should be able to do:
BankWebService.BillingServerService myService = new BankWebService.BillingServerService();
GetBillsResponse response = // <----------------- NEW
myService.getBill("1","2","3","4","5");
I want to write this C# code for getBill output:
if (errorID == 7) then do something...
Once you have the response above you can simply:
var errorID = response.Return.ErrorID;
Appendices
Plopping your SOAP into xmltocsharp I get
using System;
using System.Xml.Serialization;
using System.Collections.Generic;
namespace Xml2CSharp
{
[XmlRoot(ElementName="return")]
public class Return {
[XmlElement(ElementName="errorID")]
public string ErrorID { get; set; }
}
[XmlRoot(ElementName="getBillsResponse", Namespace="http://obrs/")]
public class GetBillsResponse {
[XmlElement(ElementName="return")]
public Return Return { get; set; }
[XmlAttribute(AttributeName="ns2", Namespace="http://www.w3.org/2000/xmlns/")]
public string Ns2 { get; set; }
}
[XmlRoot(ElementName="Body", Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
public class Body {
[XmlElement(ElementName="getBillsResponse", Namespace="http://obrs/")]
public GetBillsResponse GetBillsResponse { get; set; }
}
[XmlRoot(ElementName="Envelope", Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
public class Envelope {
[XmlElement(ElementName="Body", Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
public Body Body { get; set; }
[XmlAttribute(AttributeName="S", Namespace="http://www.w3.org/2000/xmlns/")]
public string S { get; set; }
}
}

Visual Studio has a feature that parse the XML file for you:
In Solution Explorer, right-click the name of the project that you
want to add the service to, and then click Add Service Reference.
In the Add Service Reference dialog box, click the Advanced button.
In the Service Reference Settings dialog box, click Add Web
Reference.
In the URL box, enter the URL of the Web service to use.
In the Web services found at this URL box, select the Web service to
use.
In the Web reference name field, enter a name that you will use in
your code to access the selected Web service programmatically.
Click Add Reference.
using tutorial above allows the visual studio to add the webservice into your project, and anytime you need it, you just call the webservice(Whatever you put in the web reference name field) and create an object from it (Create an instance from it), and you can easily use its methods like below:
using(WebService.MainClass obj = new WebService.MainClass())
{
int id = obj.GetId("Method Inputs");
return id;
}

You need to parse the XML to extract the field you need. There are many ways to do this. One way is to use XmlDocument();
For example for the XML you gave and the required field;
private static int ParseErrorIdField(string xml)
{
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml);
var errorIdField = xmlDoc.SelectSingleNode("//*[contains(name(),'errorID')]");
if (errorIdField == null)
{
//couldn't find field in xml
//return parsing error
return -1;
}
int errorId;
if (!int.TryParse(errorIdField.InnerText, out errorId))
{
//Could not convert field to integer
//raise error
return -1;
}
return errorId;
}

Related

C# Assigning default value to System.Uri property

I have a project which is about RESTful API and I have as a property the basic URL, on which I need to add parts each time (that's in my methods). I need (a) to declare the default (unchanged) path, and then (b) some help on how do I add to the URL. Example:
public partial class APIParamaters
{
public System.Uri URL { get; set; } = (System.Uri) "http://192.100.106.657:8811/some/part/here/version1/api"; //throws error !!!
}
This is throwing an error and I don't know how to correct.
Also, how do I later add to the URL, for example, I am trying
class MyTest
{
public string SpecialPart = "Excellent";
public APIParamaters myParams = new APIParamaters
{
URL = URL + SpecialPart + "FirstCall", //trying to do: "http://192.100.106.657:8811/some/part/here/version1/api/Excellent/FirstCall"
SomethingElse = "Ok"
//etc..
};
}
The following code means (cast this string as System.Uri) but string can not be cast as System.Uri:
(System.Uri) "http://192.100.106.657:8811/some/part/here/version1/api";
You should instantiate System.Uri:
public System.Uri URL { get; set; } = new System.Uri("http://192.100.106.657:8811/some/part/here/version1/api");

Call SOAP api form c# using action, location, endpoint & namespace

I have never used soap api.
I have requirements that i have to call soap api & send response as a json(REST) api.
I have Web Service API Location(...?wsdl), Endpoint, Namespace & Soap action.
I also have username, password & other input parameters.
I am not sure how to create soap Envelope using above info & call api from c#.
Can anyone suggest me how to do it.
This is service GetRxHistory i am trying to call https://pharmacy.esihealthcaresolutions.com:9100/v4.0/RxHistoryService.svc?wsdl/GetRxHistory
First add service reference to your project using References > Add > Service Reference. In the address field enter the url for your wsdl file:
https://pharmacy.esihealthcaresolutions.com:9100/v4.0/RxHistoryService.svc?singleWsdl
You can create the client for calling this API using:
RxHistoryServiceContractClient client = new RxHistoryServiceContractClient();
You can then call various operations on the service using the client object.
client.xxxx = xxx;
client.xxx = xxx;
In your case, it would be something like this for your username and password:
client.ClientCredentials.UserName.UserName = "your username";
client.ClientCredentials.UserName.Password = "your password";
Finally, to get a response you'd write something like this:
try
{
_Client.Open();
You'd pass your request or client object here:
GetRxHistoryResponse _Response = _Client.{MethodToGetResponse}(client);
_Client.Close();
}
catch (Exception ex)
{
}
Isn't there any way to create soap envelop from data that i have?
We could use the Message class(System.ServiceModel.Channels) static method, CreateMessage method.
I have made a demo, wish it is useful to you.
class Program
{
static void Main(string[] args)
{
Product p = new Product()
{
ID = 1,
Name = "Mango"
};
Message m=Message.CreateMessage(MessageVersion.Soap12, "mymessage", p);
MessageHeader hd = MessageHeader.CreateHeader("customheader", "mynamespace", 100);
m.Headers.Add(hd);
Console.WriteLine(m);
}
}
public class Product
{
public int ID { get; set; }
public string Name { get; set; }
}
Result.
Here is an official document.
https://learn.microsoft.com/en-us/dotnet/api/system.servicemodel.channels.message.createmessage?view=netframework-4.7.2

Building json response

I'm working on a self hosting rest api used to monitor de status of several servers.
I was tasked that, when everything is working correctly, I should only return
{"response":"ok"}
But, when there's an error on queried server, or servers, I must return
{ "response" : [ {"agent":"<server>:<port>","port":"<port>" ,"Error":"<Description of the error>"} ] }
I was thinking on building a helper class to build object on this schema and returning them over the rest api
public class HelperErrorResponseClass
{
public string agent { get; set; }
public string port { get; set; }
public string Error { get; set; }
}
This is no problem, the issue is, how to deal when everything it ok. I have this Api response helper class
public class Response
{
public string response { get; set; }
}
But I'm seeing that I'll need to change the response property to List<HelperErrorResponseClass> in order to send the error response. Do you think that, if I stringify the List<HelperErrorResponseClass> object with Json.Net it will be returned in the desired format?
Edit: Forgot to add that, I-m using Web Api to build the rest service.
UDPATE:
After further research, I found a way to work this out.
Following this post, I was able to rewrite the helper classes like this
[DataContract]
[KnownType(typeof(List<HelperErrorResponseClass>))]
public class Response
{
[DataMember]
public object response { get; set; }
}
[DataContract]
public class HelperErrorResponseClass
{
[DataMember(EmitDefaultValue = false)]
public string agent { get; set; }
[DataMember(EmitDefaultValue = false)]
public string port { get; set; }
[DataMember(EmitDefaultValue = false)]
public string error { get; set; }
}
This work to fulfill my and my client needs... except for one little thing. When I get the result from a List, and given that I added the KnownTypes directive, my response is now this
{"response":[{"__type":"HelperErrorResponseClass:#AppCommonLib","Error":"ERROR","InstanceId":"<InstanceId> : <Port>","PortType":"<PortType>"},{"__type":"HelperErrorResponseClass:#AppCommonLib","Error":"ERROR","InstanceId":"<InstanceId> : <Port>","PortType":"<PortType>"}]}
Any idea how to get rid of that __type property of the response? make that it must be explicit to only return the declared properties of the helper class?
Simplest way to deal with this is to set the return type on the handling function to string, then you can check for errors and do something like;
//pseudo code to give an idea
if (errorsList.Count() > 0)
{
return JsonConvert.SerializeObject(errorsList);
}
else
{
return JsonConvert.SerializeObject(new Response("ok"));
}
Now this being said... Unless the people providing requirements aren't at all flexible you should just redo the design. How about just returning the errors array and the person calling the API can infer that if it's length is 0 then everything is working OK. Seems pretty straight forward, right? You could also just put all the properties on one object and those fields would just come back as null or empty strings. Or you could change you serializer settings to exclude them if they don't have a value.
Keep things simple and use an anonymous type.
if (condition)
{
return JsonConvert.SerializeObject(new { response = new { agent = "x", port = "y", error = "z" }});
}
else
{
return JsonConvert.SerializeObject(new { response = "ok"});
}
More info:
https://msdn.microsoft.com/en-us/library/bb397696.aspx
I personally don't think you need a Response class, especially that it is of object type. IMHO, you've overcomplicated the very simple issue that you have. It is not only the __type, but also other info like HelperErrorResponseClass:#AppCommonLib that isn't supposed to be there.
Another Issue you have is the incorrect name of the HelperErrorResponseClass class. This is not a helper class. It is a standard data-object class.
A helper class is a class filled with static methods. It is usually used to isolate a "useful" algorithm.
This is how I would do it:
I'd get rid of the Response class.
I'd use your original simple HelperErrorResponseClass class, but rename it to something more meaningful like ErrorDetails.
I'd return the response like this:
.
if (errorsList.Count() > 0) {
return JsonConvert.SerializeObject(new { response = errorsList});
}
else {
return JsonConvert.SerializeObject(new { response = "ok"});
}
However, if you really want to stick to your updated solution, an easy way to get rid of the __type is simply removing it from the final serialized string:
if (errorsList.Count() > 0) {
string r = JsonConvert.SerializeObject(new { response = errorsList});
return r.Replace("__type", "");
}
else {
return JsonConvert.SerializeObject(new { response = "ok"});
}

Creating Login/Password in C# (compare List<> to string)

Hello im trying to create a login using wcf but somehow looks like my program dont work as I wanted ;(
public class UserService : IUserService
{
[DataMember]
public string Login { get; set; }
[DataMember]
public string Password { get; set; }
[DataMember]
public string Type { get; set; }
[DataMember]
public int ID { get; set; }
public List<UserInfo> GetUserInformation()
{
QuizDBEntities contex = new QuizDBEntities();
var UserInfo = from a in contex.UserInfoes select a;
return UserInfo.ToList();
}
}
I created
protected void Button1_Click(object sender, EventArgs e)
{
string username = TextBox1.Text;
string password = TextBox2.Text;
UserService vs = new UserService();
List<UserInfo> alfa = new List<UserInfo>();
}
I used few foreach/if loop but every time I do something wrong and my list act like its empty ( I tried grindwiev and did get all data ;( ) Anyone can help me and give hint how can I compare List to login/password ?
A WCF service isn't actually a service until it's hosted somewhere (IIS, self-hosted, etc). Simply adding the attributes [ServiceContract] and [OperationContract] do not magically make it a service.
SOAP Web services like WCF are not directly accessed by the client - the client goes through a proxy to interact with the service. This proxy can be generated automatically by Visual Studio through either Add Service Reference or the command line svcutil.exe. An easy way to do this is to create a new WCF Service Application - this will be hosted in IIS. There are different (and in my opinion better) ways to host the service, but for simplicity and sake of illustration we'll go with this one.
So let's assume you have a WCF service application up and running, and it has the code you posted above. You could then choose Add Service Reference in the VS Solution Explorer to add a service reference to your service. This will generate a proxy for you to use. The name of the proxy is usually UserServiceClient (i.e., Visual Studio adds Client to the end).
To call a method in your service with this proxy, you would do this:
UserServiceClient proxy = new UserServiceClient();
List<UserInfo> users = proxy.GetUserInformation;
This would give you a list of all the users in your database. You would probably want to either markup the UserInfo entity as a DataContract, or create a new class that has the properties in it as a DataContract - your current code doesn't do anything to set the properties it has in it, and services themselves don't do anything with properties (not to mention your code isn't setting any values for them anyway).
Now for your other question - "how can I compare List to login/password"? In a nutshell, you can't. Your List<UserInfo> is a list of UserInfo objects, and you're attempting to compare a string to this list. That won't work.
What you could do, however, is create another method in your service that would accept a username and a password and return that user's information if it is found. It might look something like below, but first let's make a DataContract to hold the UserInfo (basically moving the DataMembers from the service to a separate class:
[DataContract]
public class UserInformation
{
[DataMember]
public string Login { get; set; }
[DataMember]
public string Password { get; set; }
[DataMember]
public string Type { get; set; }
[DataMember]
public int ID { get; set; }
}
public UserInformation GetUser(string userName, string password)
{
UserInformation user = new UserInformation();
using (QuizDBEntities context = new QuizDBEntities())
{
user = (from a in context.UserInfoes
where a.UserName == userName && a.Password == password
select new UserInformation() {
Login = a.UserName,
Password = a.Password,
Type = a.Type,
ID = a.ID}).SingleOrDefault();
}
return user;
}
The UserInformation class contains the DataMembers you originally had in your service. The LINQ query selects the user that has the matching UserName and Password and populates the UserInformation class (property names are conjecture as I don't know what your UserInfo entity looks like). The SingleOrDefault() at the end selects one matching result, or if no match is found returns the default value - which in this case will be null.
You could then use it like this:
UserServiceClient proxy = new UserServiceClient();
UserInformation user = proxy.GetUser("someName", "somePassword");
proxy.Close();
if (user == null)
{
// No match was found, so do something
}
else
{
// Match was found, so proceed with what you were doing
}
All of the above is primarily for illustration purposes, but you should be able to adapt to your program's needs. I would also suggest Googling for some good tutorials on how to create and host a WCF service.

Deserializing XML where each object has a list of objects

I'm working on a WP7 app which gets and updates data on a web server. If any updates need a response, I get a list of errors that needs to be dealt with, and a list of possible choices for each error. The issue I'm having is assigning each object its appropriate list of choices. As of now I get a list of errors, and another list of all possible choices for all errors. I'd like the error object to contain the list of only its options so I can handle that.
So here's an example response:
<?xml version="1.0" encoding="utf-8"?>
<response xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<response_error_dialogs>
<error_dialog_list>
<error_dialog_choice>
<error_dialog_id>1301</error_dialog_id>
<error_dialog_message>You have changed the phone number. Select which phone number to make the primary contact number.</error_dialog_message>
<error_dialog_title>Phone Number Changed</error_dialog_title>
<error_dialog_is_set>false</error_dialog_is_set>
<error_dialog_choice_option_list>
<error_dialog_choice_option>
<error_dialog_choice_option_id>1</error_dialog_choice_option_id>
<error_dialog_choice_option_title>Home</error_dialog_choice_option_title>
</error_dialog_choice_option>
<error_dialog_choice_option>
<error_dialog_choice_option_id>2</error_dialog_choice_option_id>
<error_dialog_choice_option_title>Mobile</error_dialog_choice_option_title>
</error_dialog_choice_option>
<error_dialog_choice_option>
<error_dialog_choice_option_id>3</error_dialog_choice_option_id>
<error_dialog_choice_option_title>Work</error_dialog_choice_option_title>
</error_dialog_choice_option>
</error_dialog_choice_option_list>
</error_dialog_choice>
<error_dialog_choice>
<error_dialog_id>1303</error_dialog_id>
<error_dialog_message>You have changed the account email address. Would you like this to be the new default email?</error_dialog_message>
<error_dialog_title>Email Address Changed</error_dialog_title>
<error_dialog_is_set>false</error_dialog_is_set>
<error_dialog_choice_option_list>
<error_dialog_choice_option>
<error_dialog_choice_option_id>1</error_dialog_choice_option_id>
<error_dialog_choice_option_title>No</error_dialog_choice_option_title>
</error_dialog_choice_option>
<error_dialog_choice_option>
<error_dialog_choice_option_id>2</error_dialog_choice_option_id>
<error_dialog_choice_option_title>Yes</error_dialog_choice_option_title>
</error_dialog_choice_option>
</error_dialog_choice_option_list>
</error_dialog_choice>
</error_dialog_list>
</response_error_dialogs>
</response>
And here's the classes used:
public class ErrorDialog
{
XElement self;
public ErrorDialog() { }
public ErrorDialog(XElement errorDialog)
{
self = errorDialog;
}
public int errorDialogId
{
get { return (int)(self.Element("error_dialog_id")); }
}
public string errorDialogMessage
{
get { return (string)(self.Element("error_dialog_message")); }
}
public string errorDialogTitle
{
get { return (string)(self.Element("error_dialog_title")); }
}
public bool errorDialogIsSet
{
get { return (bool)(self.Element("error_dialog_is_set")); }
}
public List<ErrorDialogChoice> errorDialogChoice
{
get { return (List<ErrorDialogChoice>)(errorDialogChoice); }
}
public int errorDialogSelectedOption
{
get { return (int)(self.Element("error_dialog_selected_option")); }
}
}
class ErrorDialogChoice
{
XElement self;
public ErrorDialogChoice() { }
public ErrorDialogChoice(XElement errorDialogChoice)
{
self = errorDialogChoice;
}
public int errorDialogChoiceOptionId
{
get { return (int)(self.Element("error_dialog_choice_option_id")); }
}
public string errorDialogChoiceOptionTitle
{
get { return (string)(self.Element("error_dialog_choice_option_title")); }
}
}
And here's how I'm parsing it:
XElement response = XElement.Parse(data);
ErrorDialog[] dialogs = response
.Element("response_error_dialogs")
.Element("error_dialog_list")
.Elements("error_dialog_choice")
.Select(e => new ErrorDialog(e))
.ToArray();
ErrorDialogChoice[] edChoices = response
.Element("response_error_dialogs")
.Element("error_dialog_list")
.Element("error_dialog_choice")
.Element("error_dialog_choice_option_list")
.Elements("error_dialog_choice_option")
.Select(e => new ErrorDialogChoice(e))
.ToArray();
So with this example, the first error_dialog_choice object will have a List containing 3 error_dialog_choice_option objects, the second has the two error_dialog_choice_option objects, and any more that may come back. Any help is appreciated. Thanks.
You can use XML serialization to achieve this much easier:
var reader = new StringReader(xmlString);
var ser = new XmlSerializer(typeof(Response));
var result = (Response) ser.Deserialize(reader);
Using these class definitions.
[XmlType("response")]
public class Response
{
[XmlElement("response_error_dialogs")]
public ErrorDialog ErrorDialog;
}
[XmlType("response_error_dialogs")]
public class ErrorDialog
{
[XmlArray("error_dialog_list")]
public List<ChoiceErrorDialog> ChoiceList;
}
[XmlType("error_dialog_choice")]
public class ChoiceErrorDialog
{
[XmlElement("error_dialog_id")]
public int Id;
[XmlElement("error_dialog_message")]
public string Message;
[XmlElement("error_dialog_title")]
public string Title;
[XmlElement("error_dialog_is_set")]
public bool IsSet;
[XmlArray("error_dialog_choice_option_list")]
public List<Option> OptionList;
}
[XmlType("error_dialog_choice_option")]
public class Option
{
[XmlElement("error_dialog_choice_option_id")]
public int Id;
[XmlElement("error_dialog_choice_option_title")]
public string Title;
}
I am guessing there can be more types of error dialogs, and <error_dialog_choice> is just one of the possible types. In this case you could use subclassing, and list the subclasses with XmlArrayItem attributes.
You could also generate the class definitions with xsd.exe or svcutil.exe, from an .xsd or .wsdl file. xsd.exe can even infer the schema from a sample .xml file.
xsd.exe /?
svcutil.exe /?
Use the XmlSerializer built into the framework. Use attributes to map out the xml equivalent of your classes and their properties.
Example:
[XmlElement("Txn")]
public List<Transaction> Items { get; set; }
You can use the XSD.exe to generate an XSD and then generate C# code for your XSD. Use Visual Studio Command Prompt with a sample response.xml file. Eg:
c:>xsd response.xml
c:>xsd response.xsd /c /edb

Categories