How to connect to XML-RPC from c# - c#

How to connect to XML-RPC Api from c# ,
A client can interact with a Pandorabot by POST'ing to:
http://www.pandorabots.com/pandora/talk-xml
The form variables the client needs to POST are:
botid - see H.1 above.
input - what you want said to the bot.
custid - an ID to track the conversation with a particular customer. This variable is optional. If you don't send a value Pandorabots will return a custid attribute value in the element of the returned XML. Use this in subsequent POST's to continue a conversation.
How to call?

This should get you going:
public void Talk()
{
string xmlResult = null;
Result result = null; // Result declared at the end
string botId = "c49b63239e34d1"; // enter your botid
string talk = "Am I a human?";
string custId = null; // (or a value )
using (var wc = new WebClient())
{
var col = new NameValueCollection();
col.Add("botid", botId);
col.Add("input", talk);
if (!String.IsNullOrEmpty(custId))
{
col.Add("custid", custId);
}
byte[] xmlResultBytes = wc.UploadValues(
#"http://www.pandorabots.com/pandora/talk-xml",
"POST",
col);
xmlResult = UTF8Encoding.UTF8.GetString(xmlResultBytes);
result = Result.GetInstance(xmlResultBytes);
}
//raw result
Console.WriteLine(xmlResult);
// use the Result class
if (result.status == 0) // no error
{
Console.WriteLine("{0} -> {1}",
result.input, result.that);
}
else // error
{
Console.WriteLine("Error: {0} : {1}",
result.input, result.message);
}
}
[XmlRoot(ElementName="result")]
public class Result
{
static XmlSerializer ser = new XmlSerializer(typeof(Result) , "");
public Result()
{
}
public static Result GetInstance(byte[] bytes)
{
return (Result)ser.Deserialize(new MemoryStream(bytes));
}
[XmlAttribute]
public int status { get; set; }
[XmlAttribute]
public string botid { get; set; }
[XmlAttribute]
public string custid { get; set; }
[XmlElement]
public string input { get; set; }
[XmlElement]
public string that { get; set; }
[XmlElement]
public string message { get; set; }
}

Related

Iteration cannot operate on variables of type public definition for 'getenumerator'

I am doing a search in which I am making an API call and get the XML response and SerializeXmlNode and DeserializeObject to my root object. Now the problem is when I tried to loop with foreach.
I get this error below:
foreach statement cannot operate on variables of type (Model.AccountLite) because does not contain public instance definition for 'getenumerator'
I have inspected this data = JsonConvert.DeserializeObject(json); and i can see the data.
I have tried to look at this previously asked question
Search API call
public static List<AccountLite> searchAccounts(string searchString)
{
List<AccountLite> result = new List<AccountLite>();
Root data = new Root();
string[] contains = searchString.Split(' ');
RestClient client = new RestClient(baseUrl);
foreach (string contain in contains)
{
if (contain.Length < 3) continue;
RestRequest request = new RestRequest($"/xx/xx/xx/xxx/xxx/account?xx=Lite&searchString={searchString}");
String encoded = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
request.AddHeader("Authorization", "Basic " + encoded);
IRestResponse response = client.Execute(request);
string requestResponse = response.Content;
//Converting data from XML into Json and deserializet json object
try
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(requestResponse);
string json = JsonConvert.SerializeXmlNode(doc);
data = JsonConvert.DeserializeObject<Root>(json);
}
catch (Exception)
{
continue;
}
if (data?.SiebelMessageEnvelope?.ListOfAccountLite?.AccountLite == null)
continue;
//this line is the one showing error.
foreach (AccountLite item in data.SiebelMessageEnvelope.ListOfAccountLite.AccountLite)
{
bool containsBoth = true;
foreach (string contain2 in contains)
{
if (!item.Name.ToLower().Contains(contain2.ToLower()) && !item.Id.ToLower().Contains(contain2.ToLower()))
containsBoth = false;
}
if (containsBoth)
{
if (result.FirstOrDefault(i => i.Id == item.Id) == null)
{
result.Add(item);
}
}
}
}
return result;
}
Model
public class AccountLite
{
public string Id { get; set; }
public string AccountStatus { get; set; }
public string AccountTypeCode { get; set; }
public string Location { get; set; }
public string Name { get; set; }
public string SRIntegrationFlag { get; set; }
}
public class ListOfAccountLite
{
public AccountLite AccountLite { get; set; }
}
public class SiebelMessageEnvelope
{
[JsonProperty("#xmlns")]
public string Xmlns { get; set; }
public ListOfAccountLite ListOfAccountLite { get; set; }
}
public class Root
{
public SiebelMessageEnvelope SiebelMessageEnvelope { get; set; }
}
Json Object
{
"SiebelMessageEnvelope":{
"#xmlns":"",
"ListOfAccountLite":{
"AccountLite":{
"Id":"",
"AccountStatus":"",
"AccountTypeCode":"",
"Location":"",
"Name":"",
"SRIntegrationFlag":""
}
}
}
}
Your ListOfAccountLite just contains a single AccountLite. It doesn't make sense to foreach over a single object, where that object is not enumerable (meaning: implemented IEnumerable[<T>] or contains an explicit GetEnumerator() method).
There's only one object, so... just take it. Instead of
if (data?.SiebelMessageEnvelope?.ListOfAccountLite?.AccountLite == null)
continue;
foreach (AccountLite item in data.SiebelMessageEnvelope.ListOfAccountLite.AccountLite)
{
// ... do the thing
}
simply
var item = data?.SiebelMessageEnvelope?.ListOfAccountLite?.AccountLite;
if (item is null)
continue;
// ... do the thing
That said: you should probably investigate whether ListOfAccountLite in the JSON etc is meant to be an array rather than a single object.

Error when deserializing xml into array

I'm trying to deserialize xml retrieved from a web service call
using (var client = new WebClient())
{
client.UseDefaultCredentials = true;
var content = client.DownloadString("call to service");
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(List<NewCourseApply.Models.Education>));
using (TextReader textReader = new StringReader(content))
{
var e = (List<NewCourseApply.Models.Education>)serializer.Deserialize(textReader);
}
}
The xml returned from the service is:
<ArrayOfEducation xmlns="http://schemas.datacontract.org/2004/07/CovUni.Domain.Admissions" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Education><_auditList xmlns="http://schemas.datacontract.org/2004/07/CovUni.Common.Base" xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/><_apCode>670104552</_apCode><_attendanceType>FT</_attendanceType><_educationId>1</_educationId><_establishmentDetails>test school</_establishmentDetails><_fromDate>2016-11-01T00:00:00</_fromDate><_toDate>2016-11-22T00:00:00</_toDate><_ucasSchoolCode/></Education></ArrayOfEducation>
My client side object is:
[Serializable]
public class Education
{
protected int _apCode;
protected int _educationId;
protected string _establishmentDetails;
protected string _ucasSchoolCode;
protected DateTime? _fromDate;
protected DateTime? _toDate;
protected string _attendanceType;
protected string _auditList;
public int ApCode
{ get { return _apCode;}
set { _apCode = value;} }
public int EducationId
{ get { return _educationId;}
set { _educationId = value;} }
public string EstablishmentDetails
{ get { return _establishmentDetails;}
set { _establishmentDetails = value;} }
public string UcasSchoolCode
{ get { return _ucasSchoolCode;}
set { _ucasSchoolCode = value;} }
public DateTime? FromDate
{ get { return _fromDate;}
set { _fromDate = value;} }
public DateTime? ToDate
{ get { return _toDate;}
set { _toDate = value;} }
public string AttendanceType
{ get { return _attendanceType;}
set { _attendanceType = value;} }
public string AuditList
{ get { return _auditList;}
set { _auditList = value;} }
}
The error I am getting is:
There is an error in XML document (1, 2).
<ArrayOfEducation xmlns='http://schemas.datacontract.org/2004/07/CovUni.Domain.Admissions'> was not expected.
Also if I call a web service call and get the singular Education response i.e.:
<Education xmlns="http://schemas.datacontract.org/2004/07/CovUni.Domain.Admissions" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><_auditList xmlns="http://schemas.datacontract.org/2004/07/CovUni.Common.Base" xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/><_apCode>670104552</_apCode><_attendanceType>FT</_attendanceType><_educationId>1</_educationId><_establishmentDetails>test school</_establishmentDetails><_fromDate>2016-11-01T00:00:00</_fromDate><_toDate>2016-11-22T00:00:00</_toDate><_ucasSchoolCode/></Education>
Surely I just need one Simple Education class on the client side that can deserialise from the 2 examples of xml i have provided i.e. array and non array
Can some of you kind souls let me know where i'm going wrong or if there's a better way of doing this?
Many Thanks
Change the Class to
[XmlRoot("ArrayOfEducation", Namespace = "http://schemas.datacontract.org/2004/07/CovUni.Domain.Admissions")]
public class ArrayOfEducation
{
[XmlElement("Education")]
public List<ContainerEducation> education { get; set; }
}
public class ContainerEducation
{
[XmlElement(ElementName = "_apCode")]
public int _apCode { get; set; }
[XmlElement(ElementName = "_educationId")]
public int _educationId { get; set; }
[XmlElement(ElementName = "_establishmentDetails")]
public string _establishmentDetails { get; set; }
[XmlElement(ElementName = "_ucasSchoolCode")]
public string _ucasSchoolCode { get; set; }
[XmlElement(ElementName = "_fromDate")]
public DateTime? _fromDate { get; set; }
[XmlElement(ElementName = "_toDate")]
public DateTime? _toDate { get; set; }
[XmlElement(ElementName = "_attendanceType")]
public string _attendanceType { get; set; }
[XmlElement(ElementName = "_auditList", Namespace = "http://schemas.datacontract.org/2004/07/CovUni.Common.Base")]
public string _auditList { get; set; }
}
And Deserialize below way. Now, when I run the code to deserialize your XML, I do get the objects filled nicely.
XmlSerializer mySerializer = new XmlSerializer(typeof(ArrayOfEducation));
using (TextReader textReader = new StringReader(content))
{
ArrayOfEducation arrEdu = (ArrayOfEducation)mySerializer.Deserialize(textReader);
}
Update as per your comment:
If you are sure that web service is going to send single Education then you need to change the class to
[XmlRoot("ArrayOfEducation", Namespace = "http://schemas.datacontract.org/2004/07/CovUni.Domain.Admissions")]
public class ArrayOfEducation
{
[XmlElement("Education")]
public ContainerEducation education { get; set; }
}

C# loading grid with json

I'm trying to load a grid with json that I receive. This is my code in Home.cs:
private void getTickets()
{
try
{
string urlName = "tickets";
string method = "GET";
string json = null;
HttpStatusCode statusCode = HttpStatusCode.Forbidden;
Tickets data = null;
RestClient client = RequestClient.makeClient();
MakeRequest request = new MakeRequest(null, null, urlName, method);
IRestResponse response = client.Execute(request.exec());
statusCode = response.StatusCode;
json = response.Content;
var ticketWrapper = JsonConvert.DeserializeObject<TicketWrapper>(json);
if ((int)statusCode == 200)
{
gridTicket.DataSource = ticketWrapper.tickets;
}
else
{
MessageBox.Show("error");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Ticket wrapper class
class TicketWrapper
{
public IEnumerable<Tickets> tickets { get; set; }
}
Tickets class
class Tickets
{
public int id;
public string title;
public string description;
public int user_id;
public int subject_id;
}
If I debug I can see that I receive the json but ticketWrapper is null what could be wrong here?
Debug image:
Try to change public fields in Ticket class to properties:
class Tickets
{
public int id { get; set; }
public string title { get; set; }
public string description { get; set; }
public int user_id { get; set; }
public int subject_id { get; set; }
}
Also I think that IEnumerable is not the best option for serialization. Try to use List in TicketWrapper.
Additionally move your break point down, because in current position ticketWrapper will be always null (expression has not yet been executed).

Parse the JSON syntax if it is not parsed well

How do I parse the JSON data if it is not well parsed from newtonsoft json. Please refer my below code:
var web_uri = new Uri("www.example.com");
var resp = await client2.GetAsync(web_uri);
var resp_to_str = await resp.Content.ReadAsStringAsync();
var json_obj = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(resp_to_str);
Finally i parsed the JSON. now, it produces as expected.
{
"Sex": "Male",
"category": "A",
"ID": 14,
"created": "2016-03-03",
"Tag": "2340",
"members": [{
"type": "A",
"name": "fam_mem",
"state": "ca",
"Family": {
"myGuardName": "tony",
"details": [{
"address": "ca",
"type": "A"
}]
}
}]
}
**RootObject omyclass = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObject>(json_obj);**
Now i am getting error at the above line:
An exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in System.Linq.Expressions.dll but was not handled in user code
Additional information: The best overloaded method match for 'Newtonsoft.Json.JsonConvert.DeserializeObject(string)' has some invalid arguments
public class Detail
{
public string address { get; set; }
public string type { get; set; }
}
public class Family
{
public string myGuardName { get; set; }
public List<Detail> details { get; set; }
}
public class Member
{
public string type { get; set; }
public string name { get; set; }
public string state { get; set; }
public Family Family { get; set; }
}
public class RootObject
{
public string Sex { get; set; }
public string category { get; set; }
public int ID { get; set; }
public string created { get; set; }
public string Tag { get; set; }
public List<Member> members { get; set; }
}
TextBlock.Text = omyclass
I have updated the question
Hope it helps:
var web_uri = new Uri("www.example.com");
var resp = await client2.GetAsync(web_uri);
var resp_to_str = await resp.Content.ReadAsStringAsync();
RootObject omyclass = JsonConvert.DeserializeObject<RootObject>(resp_to_str); //pass the response string here.
Updated from OP's comment:
textBlock2.Text = omyclass + "----!"; will not work because omyclass is RootObject, not a string
You have to get the info you need and append it to textBlock2:
textBlock2.text = omyclass.Sex + "----!";
UPDATE 2 (OP get string as key value pair):
Usage: textBlock2.text = omyclass + "----!";
Override RootObject.ToString() and use Reflection to get properties & property values
public class RootObject
{
public string Sex { get; set; }
public string category { get; set; }
public int ID { get; set; }
public string created { get; set; }
public string Tag { get; set; }
public List<Member> members { get; set; }
public override string ToString()
{
var values = new List<string>();
foreach (var property in GetType().GetProperties())
{
values.Add(property.Name + ": " + property.GetValue(this));
}
return string.Join(", ", values);
}
}
The problem is that JsonConvert.DeserializeObject<T>(string) expects a string as an input argument -- but you are not passing in a string. You are passing in json_obj which is a dynamic returned by a previous call to deserialize:
var json_obj = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(resp_to_str);
var omyclass = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObject>(json_obj);
What JsonConvert returns from the first dynamic call is in fact a JToken containing a tree of LINQ-to-JSON tokens -- not a string. This causes the RuntimeBinderException upon making the second call.
There is no need to double-deserialize the JSON string in this manner. Just pass the resp_to_str to JsonConvert.DeserializeObject<RootObject>():
var omyclass = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObject>(resp_to_str);
Prototype fiddle.
Update
If you want to see all the fields and properties of the deserialized class in a text box, you could re-serialize it to JSON:
var reserializedJson = JsonConvert.SerializeObject(omyclass, Formatting.Indented);
textBlock2.Text = reserializedJson;
If you do
textBlock2.Text = omyclass + "----!"
You are just showing the ToString() value for your class. And since you have not overridden this method it will just show the class name.
If you don't want to re-serialize you could use the following extension method:
public static class ObjectExtensions
{
public static StringBuilder ToStringWithReflection<T>(this T obj, StringBuilder sb)
{
sb = sb ?? new StringBuilder();
if (obj == null)
return sb;
if (obj is IEnumerable)
{
sb.Append("[");
var first = true;
foreach (var item in ((IEnumerable)obj))
{
if (!first)
sb.Append(",");
sb.Append(item == null ? "" : item.ToString());
first = false;
}
sb.Append("]");
}
else
{
var type = obj.GetType();
var fields = type.GetFields();
var properties = type.GetProperties().Where(p => p.GetIndexParameters().Length == 0 && p.GetGetMethod(true) != null && p.CanRead);
var query = fields
.Select(f => new KeyValuePair<string, object>(f.Name, f.GetValue(obj)))
.Concat(properties
.Select(p => new KeyValuePair<string, object>(p.Name, p.GetValue(obj, null))));
sb.Append("{").Append(obj.GetType().Name).Append(": ");
var first = true;
foreach (var pair in query)
{
if (!first)
sb.Append(", ");
sb.Append(pair.Key).Append(": ");
if (pair.Value is IEnumerable && !(pair.Value is string))
pair.Value.ToStringWithReflection(sb);
else
sb.Append(pair.Value == null ? "null" : pair.Value.ToString());
first = false;
}
sb.Append("}");
}
return sb;
}
public static string ToStringWithReflection<T>(this T obj)
{
return obj.ToStringWithReflection(new StringBuilder()).ToString();
}
}
Then do
textBlock2.Text = omyclass.ToStringWithReflection() + "----!"
Update 2
Or, if you want to include properties from your object hierarchy recursively, you can override the ToString() method of each, like so:
public class Detail
{
public string address { get; set; }
public string type { get; set; }
public override string ToString() { return this.ToStringWithReflection(); }
}
public class Family
{
public string myGuardName { get; set; }
public List<Detail> details { get; set; }
public override string ToString() { return this.ToStringWithReflection(); }
}
public class Member
{
public string type { get; set; }
public string name { get; set; }
public string state { get; set; }
public Family Family { get; set; }
public override string ToString() { return this.ToStringWithReflection(); }
}
public class RootObject
{
public string Sex { get; set; }
public string category { get; set; }
public int ID { get; set; }
public string created { get; set; }
public string Tag { get; set; }
public List<Member> members { get; set; }
public override string ToString() { return this.ToStringWithReflection(); }
}
Then the ToString() output will be:
{RootObject: Sex: Male, category: A, ID: 14, created: 2016-03-03, Tag: 2340, members: [{Member: type: A, name: fam_mem, state: ca, Family: {Family: myGuardName: tony, details: [{Detail: address: ca, type: A}]}}]}
You Can Use a Custom Json formatter and parse it the way you want.It can be implemented at framework level so that all data is parsed according to your requirements.Inherit the custom formatter from MediaTypeFormatter.
Implement the virtual and abstract functions of MediaTypeFormatter.Pasre the data according to your requirements here.
I have implemented the formatter this way and it even parses complex data to match the data that i want.
When serializing complex Json if not deserialized correctly the value becomes null.For deserializing complex Json use JsonMediaTypeFormatter.
My Example
In your config File add
config.Formatters.Clear();
config.Formatters.Insert(0, new JsonNetFormatterDecide()); // My custom formatter
config.Formatters.Insert(1, new JsonMediaTypeFormatter());//Default Formatter
config.MapHttpAttributeRoutes();`
So i used my custom formatter for all get operations.Format the data as i want and sent to client.The problem i faced was while accepting complex Json data(such as Jsonarrays within Jsonobjects). So in my config file i added the JsonMediaTypeFormatter() in index 1 of Config.formatters.
In my custom formatter
public override bool CanReadType(Type type)
{
return false;
}
This makes JsonMediaTypeFormatter which is a very popular formatter to Deserialize complex data to do the deserialization.
The bottomline is that you can use JsonMediaTypeFormatter toDeserialize complex Json Data
It has some predefined functions to deserialize.
By looking at your Json it seems as if Json arrays within Json objects is the reason why your Json is not deserialing correctly
If you plan to write your custom Json formatter ,You can implement at framework level like
public class JsonNetFormatterDecide : MediaTypeFormatter
{
//......
public override bool CanReadType(Type type)
{
return false; //this causes the // project to use the second formatter in the config file ie,JsonMediaTypeFormatter or the //default Json Formatter
}
}

Error Message of ResponsStatus should not be null when error is thrown and message is provided

I am using ServiceStack and I am having trouble getting back the error message in the ResponseStatus when an error is thrown.
My service Requests/Responses are named according to the naming convention required by ServiceStack.
REQUEST:
namespace DataDictionary.ServiceModel
{
// Create the name of the Web Service (i.e. the Request DTO)
[RestService("/receivables/{id}", "GET")]
[RestService("/years/{year}/processes/{processname}/receivables/{name}", "GET")]
[DataContract]
public class ReceivableRequest
{
[DataMember]
public int id { get; set; }
[DataMember]
public int year { get; set; }
[DataMember]
public string processname { get; set; }
[DataMember]
public string name { get; set; }
}
}
RESPONSE:
namespace DataDictionary.ServiceModel
{
[DataContract]
public class ReceivableRequestResponse : IHasResponseStatus
{
public ReceivableRequestResponse()
{
this.ResponseStatus = new ResponseStatus();
}
[DataMember]
public int id { get; set; }
[DataMember]
public string name { get; set; }
[DataMember]
public Uri process { get; set; }
[DataMember]
public Uri year { get; set; }
#region IHasResponseStatus Members
[DataMember]
public ResponseStatus ResponseStatus {get; set;}
#endregion
}
}
SERVICE:
namespace DataDictionary.ServiceInterface
{
// Create the Web Service implementation
public class ReceivableService : RestServiceBase<ReceivableRequest>
{
private readonly IRepository<Receivable> m_receivableRepository;
public ReceivableService(IRepository<Receivable> receivableRepository)
{
m_receivableRepository = receivableRepository;
}
public override object OnGet(ReceivableRequest request)
{
if (request != null && request.id > 0)
{
return GetSpecificReceivable(request.id);
}
}
private object GetSpecificReceivable(int id)
{
Receivable receivable = m_receivableRepository.SingleOrDefault(rec => rec.Id == id);
if (receivable != null)
{
return receivable;
}
else
{
throw new HttpError(HttpStatusCode.NotFound, new ArgumentException(string.Format("Could not find receivable with id {0}.", id)));
}
}
}
}
The error message is returned when I unit test the service
[Test]
public void Can_GET_Receivable_Not_Found_Successfully()
{
//given
var year = new Year { Name = 2006 };
IRepository<Year> repository = Kernel.Get<IRepository<Year>>();
repository.Add(year);
var process = new Process { Name = "TEST", Year = year };
year.AddProcess(process);
var receivable = new Receivable { Name = "File1" };
process.AddReceivable(receivable);
repository.Update(year);
var service = Kernel.Get<ReceivableService>();
var request = new ReceivableRequest { year = 2011, processname = "TEST", name = "File1" };
//when
try
{
var entity = (Receivable)service.OnGet(request);
}
//then
catch (HttpError e)
{
e.Message.ShouldEqual("Could not find receivable File1 from process TEST in year 2011.");
e.StatusCode.ShouldEqual(HttpStatusCode.NotFound);
}
}
However, when I send requests using fiddler, I would expect the error message to be returned in the JSON,
The status code 404 is available but there is no error message.
How can I get access to the error message?

Categories

Resources