I'm using C# and have created an object to send to a JSON service that looks like this:
public class SendRequest
{
public string id { get; set; }
public string case { get; set; }
public string method { get; set; }
public Volume value { get; set; }
}
public class Volume
{
public int level;
public bool mute;
}
I can code hint when setting the sub object:
var _req = new SendRequest();
_req.value.mute = false;
_req.value.level = 50;
But when the program is run, the sub-object itself is null (_req.value = null) and the two items under that object don't show.
Why is this happening?
You need to initialize the "value" to something.
Add this constructor to your SendRequest class:
public SendRequest(){ value = new Volume(); }
You can use object initializers
var _req = new SendRequest()
{
value = new Volume()
{
mute = false,
level = 50,
},
};
at this point method is null
public string method { get; set; }
longer but what you can do is
private string method = string.empty;
public string Method { get {return method;} set {method = value;} }
value is just a bad name
private Volume volume = new Volume();
public Volume Volume { get {return volume;} set {volume = value;} }
or
volume = new Volume (mute = false, value.level = 50);
Related
I have to an upper class with nested classes
public class Preferences
{
public FunctionClass function { get; set; } = new FunctionClass();
public class FunctionClass
{
public string programfolder { get; set; } = "";
...
}
public LoggerClass logger { get; set; } = new LoggerClass();
public class LoggerClass
{
public string logFolder { get; set; } = "Log";
...
}
public OptionClass options { get; set; } = new OptionClass();
public class OptionClass
{
public bool showGraphics { get; set; } = true;
...
}
public MqttSpSetupClass MqttSpSetup { get; set; } = new MqttSpSetupClass();
public class MqttSpSetupClass
{
public string strAddress { get; set; } = "localhost";
...
}
}
so I want reflection to cycle on all member of each inner class
PropertyInfo[] props_Outer = typeof(IoAppPreferences).GetProperties();
int counter = 0;
foreach (PropertyInfo prop_Upper in props_Outer)
{
var sName_Outer = prop_Upper.Name;
var val_Outer = props_Outer.GetValue(counter ++);
PropertyInfo[] properties_Inner;
switch (sName_Outer.ToUpper())
{
case "DIMS": properties_Inner = typeof(IoAppPreferences.DimsClass).GetProperties(); break;
...
}
foreach (PropertyInfo prop_Inner in properties_Inner)
{
var sName = prop_Inner.Name;
//prefs.function
var sVal = prop_Inner.GetValue(val_Outer);<------ERROR
switch (prop_Inner.Name.ToUpper())
{
...
}
}
I get an error where I put the arrow. And the reason is that val_Outer is FunctionClass function while if I hardcode prefs.function it is ok. Of course, I can put a switch per each one, but my question is: is there a better way to solve it?
I have seen this solution but can't fit to my needs
You got error because val_Outer is wrong instance. You are trying to get value out of counter integer props_Outer.GetValue(counter ++)
If your goal is to get property values from nested classes you must have instance of Preferences object:
var appPreferences = new Preferences();
var propsOuter = appPreferences.GetType().GetProperties();
foreach (var po in propsOuter)
{
var valueOuter = po.GetValue(appPreferences);
Console.WriteLine($"{po.Name}");
if (valueOuter == null) continue;
var propsInner = valueOuter.GetType().GetProperties();
foreach (var pi in propsInner)
{
var valueInner = pi.GetValue(valueOuter);
Console.WriteLine($"{pi.Name}: {valueInner}");
}
}
But getting values through reflection is pretty much useless if you already have object instance.
The propblem: There is no "Name" field in the object or csv file, yet CsVHelper keeps looking for "Name" in the header. So why is it tripping there and what are some fixes?
When trying to build objects from a csv file, the following error comes up:
CsvHelper.HeaderValidationException: Header with name 'Name' was not found. If you are expecting some headers to be missing and want to ignore this validation, set the configuration HeaderValidated to null. You can also change the functionality to do something else, like logging the issue.
at CsvHelper.Configuration.ConfigurationFunctions.HeaderValidated(Boolean isValid, String[] headerNames, Int32 headerNameIndex, ReadingContext context)
I have tried setting HeaderValidated to null, but got the same results.
The header of the csv:
Id|Title|Description|AssignedToUserId|SourceUserId|DateCreated|DateAssigned|DateCompleted|Notes
The parsing code:
private static IEnumerable<T> GetCSVData<T>(string fullFileName)
{
PrintMembers<T>();
using (var reader = new StreamReader(fullFileName))
{
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
csv.Configuration.HasHeaderRecord = true;
csv.Configuration.IncludePrivateMembers = false;
csv.Parser.Configuration.Delimiter = "|";
var records = csv.GetRecords<T>().ToList();
return records;
}
}
}
A quick function for listing the public properties and fields of the class (T) being passed in outputs the following:
Properties...
Id
AssignedToUserId
SourceUserId
Title
Description
AssignedTo
Source
DateCreated
DateAssigned
DateCompleted
RelatedTasks
Notes
Fields...
[None]
They all have getters and setters.
EDIT
The IntermediateTask is the generic being fed into GetCSVData(). It has a default constructor. IntermediateTask is internal, but is in the same assembly as GetCSVData().
Code for the class(es) in question:
internal class IntermediateTask : Task
{
private int _Id;
new public int Id
{
get { return _Id; }
set { _Id = value; }
}
private int _AssignedToUserId;
public int AssignedToUserId
{
get { return _AssignedToUserId; }
set
{
_AssignedToUserId = value;
base.AssignedTo = userManager.Get(_AssignedToUserId);
}
}
private int _SourceUserId;
public int SourceUserId
{
get { return _SourceUserId; }
set
{
this._SourceUserId = value;
base.Source = userManager.Get(_SourceUserId);
}
}
public IntermediateTask() : base("", "", new IntermediateUser(), new IntermediateUser())
{
}
}
public class Task
{
public Task(string title, string description, User assignedTo, User source, DateTime? dateCreated = null, int id = 0)
{
this.RelatedTasks = new List<Task>();
this.Title = title;
this.Description = description;
this.AssignedTo = assignedTo;
this.Source = source;
this.DateCreated = dateCreated ?? DateTime.Now;
this.Id = id;
}
private int _Id;
public int Id
{
get { return _Id; }
protected set { _Id = value; }
}
public string Title { get; set; }
public string Description { get; set; }
public User AssignedTo { get; set; }
public User Source { get; set; }
public DateTime DateCreated { get; set; }
public DateTime? DateAssigned { get; set; }
public DateTime? DateCompleted { get; set; }
public IList<Task> RelatedTasks { get; set; }
public string Notes { get; set; }
override public string ToString()
{
return $"Id: {Id}; Title: {Title}";
}
}
In my case it complained about AssignedTo missing, but that is actually a property in the class that is not in the csv, so I had to add these two lines to make it work:
csv.Configuration.HeaderValidated = null;
csv.Configuration.MissingFieldFound = null;
I don't know why it would come up with 'Name' unless you have something different.
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; }
}
I'm currently trying to make a form that sends 2 class, then receives the data, uses them but want to shoot them back to the original form. Any idea on how I could?
This is when i call my second form:
var tmp = new Pjeu(P1,P2);
tmp.Show();
P are Players. Here's the class:
//---
public class Player
{
public string Name;
public int pts1;
public int pts2;
public int num;
};
And I receive the data like this:
label3.Text = P1.Name;
label5.Text=P2.Name;
I want to use P1.pts or shoot back a number to it. Is it possible the way I do?
You can use multi way:
public class Player
{
public void Pa(string p1,string p2)
{
p1 = Name;
p2 = Name;
}
public string pass1(int ps1)
{
return ps1.ToString();
}
public string pass2(int ps2)
{
return ps2.ToString();
}
public string Name { get; set; }
public int pts1 { get; set; }
public int pts2 { get; set; }
public int num { get; set; }
}
and call:
Player p = new Player();
p.Pa(Label1.Text, Label2.Text);
Label1.Text = p.pts1.ToString();
Label2.Text = p.pts2.ToString();
I know I can use reflection to set a objects property like this below.
public void SaveContent(string propertyName, string contentToUpdate, string corePageId)
{
var page = Session.Load<CorePage>(corePageId);
Type type = page.GetType();
PropertyInfo prop = type.GetProperty(propertyName);
prop.SetValue(page, contentToUpdate, null);
}
I'm having these classes below:
public class CorePage
{
public string BigHeader { get; set; }
public List<BigLinks> BigLinks { get; set; }
}
public class BigLinks
{
public string TextContent { get; set; }
}
My SaveContent()-method works obviously when the property to set is, for example public string BigHeader { get; set; }
But how can I do this if the the property I want to set is in the property:
public List<BigLinks> BigLinks { get; set; }
If public List<BigLinks> BigLinks { get; set; } is a list of 5 BigLinks objects, how can a set the value of, for example the third objects public string TextContent { get; set; }?
You have to get the property value using reflection and change the desired value like this:
var c = new CorePage() { BigLinks = new List<BigLinks> { new BigLinks { TextContent = "Y"}}};
var r = typeof(CorePage).GetProperty("BigLinks").GetGetMethod().Invoke(c, null) as List<BigLinks>;
r[0].TextContent = "X";
If you don't know the type of list item:
var itemInList = (typeof(CorePage).GetProperty("BigLinks").GetGetMethod().Invoke(c, null) as IList)[0];
itemInList.GetType().GetProperty("TextContent").SetValue(itemInList, "XXX", null);
Another option is casting to dynamic:
var itemInList = (typeof(CorePage).GetProperty("BigLinks").GetGetMethod().Invoke(c, null) as dynamic)[0].TextContent = "XXXTTT";