C# Custom Property Attribute Reflection - c#

Building an OpenGraph .NET Parser but stuck in property binding. I simple fetch the HTML Document and parse it using HtmlAgilityPack. After that I want to check each Node for the specific OpenGraph Key:
Custom Attribute
public class OpenGraphAttribute : Attribute
{
public string Name { get; set; }
public OpenGraphAttribute(string name)
{
Name = name;
}
}
Container Class
public class OGVideoContainer
{
[OpenGraphAttribute("og:video:url")]
public string DefaultUrl { get; set; }
[OpenGraphAttribute("og:video:secure_url")]
public string SecureUrl { get; set; }
[OpenGraphAttribute("og:video:type")]
public string Type { get; set; }
[OpenGraphAttribute("og:video:width")]
public string Width { get; set; }
[OpenGraphAttribute("og:video:height")]
public string Height { get; set; }
[OpenGraphAttribute("og:video:url")]
public string Url { get; set; }
}
Parser
public OGVideoContainer ParseVideo(HtmlDocument doc)
{
var result = new OGVideoContainer();
var parseableAttr = typeof(OGVideoContainer).GetProperties();
foreach (var prop in parseableAttr)
{
var ca = prop.GetCustomAttributes(true).ElementAtOrDefault(0) as OpenGraphAttribute;
if (doc.DocumentNode.SelectSingleNode(String.Format("/html/head/meta[#property='{0}']", ca.Name)) != null)
{
// i am stuck here how can i access the result.propery value?
}
}
return result;
}
But stuck at the result.parameter binding. I have to assign result.DefaultUrl with the corresponding custom attribute name value. How can this be done?
Thanks for any help.

Use prop.GetValue(result) to get the property value.

Thanks. The Setter can be reflected as follows:
var targets = result.GetType().GetProperties();
targets.FirstOrDefault(m => m.Name == prop.Name).SetValue(result, "Nice String here");

Related

Add class properties from the List and put them into the string together with a comma as a delimiter

Although the thing I want to do seems be really trivial I can not find a way to achieve what I want. I know there exist multiple questions how to put class properties into the list together and separate it by a comma like that on SO, but none of them seemed to be relevant to my case.
I have a class Form defined as follows:
public class Form
{
public string CustomerName { get; set; }
public string CustomerAdress { get; set; }
public string CustomerNumber { get; set; }
public string OfficeAdress { get; set; }
public string Date { get; set; }
public Boolean FunctionalTest { get; set; }
public string Signature { get; set; }
public Form()
{
}
}
In the MainPage.xaml.cs, I create a List<Form> with the Form class properties and subsequently I would like to create a string with all of those class properties separated by a comma. For that case I use basic Join method with Select which converts any kinds of objects to string.
I do that by createCSV method inside MainPage.xaml.cs :
void createCSV()
{
var records = new List<Form>
{
new Form {CustomerName = customerName.Text,
CustomerAdress = customerAdress.Text,
CustomerNumber = customerNumber.Text,
OfficeAdress = officeAdress.Text,
Date = date.Date.ToString("MM/dd/yyyy"),
FunctionalTest = testPicker.ToString()=="YES" ? true : false,
Signature = signature.Text
}
};
string results = String.Join(",", (object)records.Select(o => o.ToString()));
}
The problem is instead of the desirable outcome which is:"Mark Brown,123 High Level Street,01578454521,43 Falmouth Road,12/15/2020,false,Brown"
I am getting: "System.Linq.Enumerable+SelectListIterator'2[MyApp.Form,System.String]"
PS. As you have noticed I am newbie in C#. Instead of non constructive criticism of the code, please for a valuable reply which would help me to understand what am I doing wrong.
Thanks in advance
In the Form class, You can override the ToString() method and use System.Reflection to get your comma string.
Form.cs
public class Form
{
public string CustomerName { get; set; }
public string CustomerAdress { get; set; }
public string CustomerNumber { get; set; }
public string OfficeAdress { get; set; }
public string Date { get; set; }
public bool FunctionalTest { get; set; }
public string Signature { get; set; }
public override string ToString()
{
string modelString = string.Empty;
PropertyInfo[] properties = typeof(Form).GetProperties();
foreach (PropertyInfo property in properties)
{
var value = property.GetValue(this); // you should add a null check here before doing value.ToString as it will break on null
modelString += value.ToString() + ",";
}
return modelString;
}
}
Code
List<string> CSVDataList = new List<string>();
List<Form> FormList = new List<Form>();
...
foreach (var data in FormList)
{
CSVDataList.Add(data.ToString());
}
Now you have a list of string CSVDataList with each Form object's data in comma style
P.S.
for DateTime
var value = property.GetValue(this);
if(value is DateTime date)
{
modelString += date.ToString("dd.MM.yyyy") + ",";
}

How to Json Element is present or not using C# (With Newtonsoft NugetPackages)

I have Json file, where i am trying the parse the data of the json file for database update with some data contions, Currently i want to check an particular element exists or not.In my current Json data, Under the Operation Node the accessRef can exist in the file or may not exist completely.So how would check the condtion. Kindly suggest me.
Below is the Json file
{
"PLMXML":{
"language":"en-us",
"time":"16:50:15",
"schemaVersion":"6",
"author":"Development(-2069033203)",
"date":"2020-05-22",
"Header":{
"id":"id1",
"traverseRootRefs":"#id6",
"transferContext":"CLM"
},
"Operation":{
"id":"id21",
"name":"PD_Op2",
"subType":"BS4_BaOP",
"accessRefs":"#id18",
"catalogueId":"70700000209604",
"ApplicationRef":{
"application":"Teamcenter",
"label":"yDX58d4FIpN8QD",
"version":"yDX58d4FIpN8QD"
}
}
}
}
Below the Code which i am trying perform
public void Readjsonfile(string jsondata)
{ var message = JsonConvert.DeserializeObject<plmxmldatamodel>(jsondata);
Console.WriteLine("RootRef: " + message.PLMXML.Header.traverseRootRefs);
Console.WriteLine("OccurenceId: "+message.PLMXML.ProductView.Operation.id);
// I want to check whtether is accessrefs is present or not inside the Operation element
}
//fuction1
public class Header
{ public string traverseRootRefs { get; set; }
}
//fuction2
public class Operation
{ public string id { get; set; }
public string name { get; set; }
public string subType { get; set; }
public string accessRefs { get; set; }
public string catalogueId { get; set; }
}
//fuction3
public class PLMXML
{
public string language { get; set; }
public Header Header { get; set; }
public Operation Operation { get; set; }
}
//fuction4
public class plmxmldatamodel
{
public PLMXML PLMXML { get; set; }
}
If empty/null value for accessRefs is a valid situation (i.e. ... "'accessRefs':, ...") then you can use JSON Path to check if it exists:
var jObj = JObject.Parse(jsondata);
if(jObj.SelectToken("$.PLMXML.Operation.accessRefs") != null)
{
// do what you want to do if accessRefs exists
}
Otherwise just check for message.PLMXML.ProductView.Operation.accessRefs not being null:
if(message.PLMXML.ProductView.Operation.accessRefs != null)
{
// do what you want to do if accessRefs exists
}

Trying to Use XDocument and LINQ

I am still learning how to use LINQ and now I'm struggling with this situation.
I have this XML file (example)
<Results>
<PrxComissao>
<Id>0</Id>
<NumErro>0</NumErro>
<RetCode>0</RetCode>
<IdEvento>0</IdEvento>
<ExecutionTimeMilliseconds>63596143450994.227</ExecutionTimeMilliseconds>
<ExecutionTimeSeconds>63596143450.994225</ExecutionTimeSeconds>
<CodComissao>CFE</CodComissao>
<Montante>20.00</Montante>
<Percentagem>0.0000</Percentagem>
<MntMin>0.00</MntMin>
<MntMax>0.00</MntMax>
<Nome>CFE</Nome>
<Descricao>Custo Factura Estrangeiro</Descricao>
</PrxComissao>
<PrxComissao>
<Id>0</Id>
<NumErro>0</NumErro>
<RetCode>0</RetCode>
<IdEvento>0</IdEvento>
<ExecutionTimeMilliseconds>63596143450994.227</ExecutionTimeMilliseconds>
<ExecutionTimeSeconds>63596143450.994225</ExecutionTimeSeconds>
<CodComissao>CFE</CodComissao>
<Montante>20.00</Montante>
<Percentagem>0.0000</Percentagem>
<MntMin>13.00</MntMin>
<MntMax>123.00</MntMax>
<Nome>CFE</Nome>
<Descricao>www</Descricao>
</PrxComissao>
</Results>
And now what I want to do is get all the XML elements inside the "PrxComissao", and then assign them to my class. This is the code I was trying
XDocument xDoc = XDocument.Parse(resultado);
List<PrxComissao> lstPrxComissao = xDoc.Elements("Results")
.Elements("PrxComissao")
.Elements()
.Select(BL_CartaoCredito.Utils.Data.Converter.FromXElement<PrxComissao>)
.ToList();
ObjAuxResult = lstPrxComissao;
What I am trying to do with this Converter.FromXElement<PrxComissao> is get all that elements and assign them.
Here is my class
public class PrxComissao
{
public string CodComissao { get; set; }
public string Montante { get; set; }
public string Percentagem { get; set; }
public string MntMin { get; set; }
public string MntMax { get; set; }
public string Nome { get; set; }
public string Descricao { get; set; }
public string TipoImposto { get; set; }
public string ComFinanciamento { get; set; }
public string iActivo { get; set; }
public string UtlModificacao { get; set; }
public string DtModificacao { get; set; }
public string UtlCriacao { get; set; }
public string DtCriacao { get; set; }
}
public static T FromXElement<T>(XElement element) where T : class, new()
{
T value = new T();
foreach (var subElement in element.Elements())
{
var field = typeof(T).GetField(subElement.Name.LocalName);
field.SetValue(value, (string)subElement);
}
return value;
}
So now I have two problems. First, I can't get to the elements inside PrxComissao always returns me nothing and then is my LINQ Select correct ? Or is there a better way ?
Start with the Descendants assuming your convertor takes an XElement:
List<PrxComissao> lstPrxComissao = xDoc.Descendants()
.Elements("PrxComissao")
.Select(el => BL_CartaoCredito.Utils.Data.Converter.FromXElement<PrxComissao>(el))
.ToList();
and then (untested) ...
public static T FromXElement<T>(XElement element) where T : class, new()
{
var typeOfT = typeof(T);
T value = new T();
foreach (var subElement in element.Elements())
{
var prop = typeOfT.GetProperty(subElement.Name.LocalName);
if(prop != null)
{
prop.SetValue(value, subElement.Value);
}
}
return value;
}
Currently, your code passes individual child elements of <PrxComissao> to the converter method. I believe you want to pass XElement that references <PrxComissao> instead :
List<PrxComissao> lstPrxComissao =
xDoc.Elements("Results")
.Elements("PrxComissao")
.Select(o => BL_CartaoCredito.Utils
.Data
.Converter
.FromXElement<PrxComissao>(o)
)
.ToList();
Besides, your class uses properties instead of field, so the corresponding reflection method supposed to be used here is GetProperty(), not GetField().

Parse XML with Linq with multiple child elements

This is my first question on SO, please let me know if I am doing anything wrong!
I am trying to parse an XML similar to this:
<LiveUpdate>
<CityID>F0A21EA2</CityID>
<CityName>CityTown</CityName>
<UserName>john</UserName>
<ApplicationDetails>
<ApplicationDetail
Application="AC"
Licensed="true"
Version="2015.2"
Patch="0001"
/>
<ApplicationDetail
Application="AP"
Licensed="true"
Version="2015.2"
Patch="0002"
/>
</ApplicationDetails>
</LiveUpdate>
I have classes that look like this:
public class Client
{
public string cityID { get; set; }
public string cityName { get; set; }
public string userName { get; set; }
public List<Apps> appList { get; set; }
}
public class Apps
{
public string app { get; set; }
public string licensed { get; set; }
public string version { get; set; }
public string patch { get; set; }
}
I need to be able to have a client class with a list of all the application details to be iterated over.
So far the best I've come up with is:
XDocument xml = XDocument.Load(#"C:\blah\Desktop\1.xml");
var liveUpdate = xml.Root;
var clients = (from e in liveUpdate.Elements()
select new Client()
{
cityID = e.Element("CityID").Value,
cityName = e.Element("CityName").Value,
userName = e.Element("UserName").Value,
appList = e.Elements("ApplicationDetails")
.Select(a => new Apps()
{
app = a.Element("Application").Value,
licensed = a.Element("Licensed").Value,
version = a.Element("Version").Value,
patch = a.Element("Patch").Value
}).ToList()
});
However, I'm currently running into an error that says Object reference not set to an instance of an object.
I've seen some similar examples on here, but not that deal with data before the multiple children.
I'm fairly new to XML and Linq so any help here would be greatly appreciated!
Your XML only contains one LiveUpdate tag, so rather than iterating over all of the elements inside of it, you just want to look at the Root element.
In ApplicationDetails, Application, Licensed and such are attributes, not elements. Use .Attribute() to access them.
ApplicationDetails is a single tag, and inside it you have ApplicationDetail tags.
There is no DateTime element in your LiveUpdate tag.
This works:
var liveUpdate = xml.Root;
var e = liveUpdate;
var clients = new Client()
{
cityID = e.Element("CityID").Value,
cityName = e.Element("CityName").Value,
userName = e.Element("UserName").Value,
//dateTime = e.Element("DateTime").Value,
appList = e.Element("ApplicationDetails").Elements("ApplicationDetail")
.Select(a => new Apps()
{
app = a.Attribute("Application").Value,
licensed = a.Attribute("Licensed").Value,
version = a.Attribute("Version").Value,
patch = a.Attribute("Patch").Value
}).ToList()
};
Since you have already defined a class into which you wish to deserialize, you can use XmlSerializer to deserialize it for you.
First, let's rename some of your property names to more closely match the XML and c# naming conventions:
[XmlRoot("LiveUpdate")]
public class Client
{
public string CityID { get; set; }
public string CityName { get; set; }
public string UserName { get; set; }
[XmlArray("ApplicationDetails")]
[XmlArrayItem("ApplicationDetail")]
public List<Apps> AppList { get; set; }
}
public class Apps
{
[XmlAttribute]
public string Application { get; set; }
[XmlAttribute]
public bool Licensed { get; set; }
[XmlAttribute]
public string Version { get; set; }
[XmlAttribute]
public string Patch { get; set; }
}
Then add the following extension methods:
public static class XmlSerializationHelper
{
public static T LoadFromXML<T>(this string xmlString)
{
using (StringReader reader = new StringReader(xmlString))
{
object result = new XmlSerializer(typeof(T)).Deserialize(reader);
if (result is T)
{
return (T)result;
}
}
return default(T);
}
public static T LoadFromFile<T>(string filename)
{
using (var fs = new FileStream(filename, FileMode.Open))
{
object result = new XmlSerializer(typeof(T)).Deserialize(fs);
if (result is T)
{
return (T)result;
}
}
return default(T);
}
}
Now you can deserialize from your XML file as follows:
string fileName = #"C:\blah\Desktop\1.xml";
var client = XmlSerializationHelper.LoadFromFile<Client>(fileName);
I manually updated your Client class to map correctly to the provided XML, but if you wanted to do it automatically, see here: Generate C# class from XML.

method declared as public not found

In my common.cs class I have the below declarations for a list based on a class:
public static List<edbService> edb_service;
public class edbService
{
public string ServiceID { get; set; }
public string ServiceName { get; set; }
public string ServiceDescr { get; set; }
public string ServiceInterval { get; set; }
public string ServiceStatus { get; set; }
public string ServiceUrl { get; set; }
public string SourceApplication { get; set; }
public string DestinationApplication { get; set; }
public string Function { get; set; }
public string Version { get; set; }
public string userid { get; set; }
public string credentials { get; set; }
public string orgid { get; set; }
public string orgunit { get; set; }
public string customerid { get; set; }
public string channel { get; set; }
public string ip { get; set; }
}
I have a public method to populate the list from xml data files declared like this in the same class (common.cs):
#region PublicMethods
public List<edbService> populateEDBService(string xmlDataFile)
{
try
{
XElement x = XElement.Load(global::EvryCardManagement.Properties.Settings.Default.DataPath + xmlDataFile);
// Get global settings
IEnumerable<XElement> services = from el in x.Descendants("Service")
select el;
if (services != null)
{
edb_service = new List<edbService>();
foreach (XElement srv in services)
{
edbService edbSrv = new edbService();
edbSrv.ServiceID = srv.Element("ServiceID").Value;
edbSrv.ServiceName = srv.Element("ServiceName").Value;
edbSrv.ServiceDescr = srv.Element("ServiceDescr").Value;
edbSrv.ServiceInterval = srv.Element("ServiceInterval").Value;
edbSrv.ServiceStatus = srv.Element("ServiceStatus").Value;
edbSrv.ServiceUrl = srv.Element("ServiceUrl").Value;
foreach (XElement ServiceHeader in srv.Elements("ServiceHeader"))
{
edbSrv.SourceApplication = ServiceHeader.Element("SourceApplication").Value;
edbSrv.DestinationApplication = ServiceHeader.Element("DestinationApplication").Value;
edbSrv.Function = ServiceHeader.Element("Function").Value;
edbSrv.Version = ServiceHeader.Element("Version").Value;
foreach (XElement ClientContext in ServiceHeader.Elements("ClientContext"))
{
edbSrv.userid = ClientContext.Element("userid").Value;
edbSrv.credentials = ClientContext.Element("credentials").Value;
edbSrv.orgid = ClientContext.Element("orgid").Value;
edbSrv.orgunit = ClientContext.Element("orgunit").Value;
edbSrv.customerid = ClientContext.Element("customerid").Value;
edbSrv.channel = ClientContext.Element("channel").Value;
edbSrv.ip = ClientContext.Element("ip").Value;
}
}
edb_service.Add(edbSrv);
}
}
}
catch (Exception ex)
{
/* Write to log */
Common.logBuilder("CustomerCreate : Form --> CustomerCreate <--", "Exception", Common.ActiveMQ,
ex.Message, "Exception");
/* Send email to support */
emailer.exceptionEmail(ex);
}
return edb_service;
}
but the problem is, in my calling class when I try to have a list returned from this method, it is not found - I get a compile error that an object reference is required.
I am trying to call it like this:
Common.edbService edb_service = Common.populateEDBService("CardUpdate.xml");
and I get the below error:
An object reference is required for the non-static field, method, or property 'EvryCardManagement.Common.populateEDBService(string)'
What am I doing wrong?
I would like to have a generic method that can be called from several classes (which run async after being instantiated by background workers on my form)
You can try making your method as static.
public static List<edbService> populateEDBService(string xmlDataFile)
{
//Your code here
....
}
Now you can call this method from all the other classes by using common.populateEDBService();
You need either to create the class static, or to create an object to call it.
class edbService { }
public static void Main() {
//this is error
edbService.populateEDBService("");
//this is correct
edbService s = new edbService();
s.populateEDBService("");
}
The last line in my example shows the object reference required by the compiler. The s variable here is the object reference.
Are there any missing values in your XML? The.Value property won't work if the value is missing. So if ServiceID is missing then srv.Element("ServiceID").Value; will cause an error. You can get it to return an empty string for missing values, for example, by instead using (string)srv.Element("ServiceID");

Categories

Resources