Trying to Use XDocument and LINQ - c#

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().

Related

Parse complex xml to fetch particular node text in c#

Parsing XML file using SSIS / C#
operations like fetching record count from trailer , TIN from body and store into a variable or somewhere temporarily(your suggestions please) for further processing. I don't want to store it in a table.
Please find the sample xml mentioned below
<ACOParticipantData xmlns:xsi="">
<Header>
<HeaderCode>HDR_PFPRVDR</HeaderCode>
<FileCreationDate>20160101</FileCreationDate>
<ACOProgCode>21</ACOProgCode>
</Header>
<Participants>
<Participant>
<ACO_ID>V199</ACO_ID>
<TIN>123456789</TIN>
<Old_TIN>987654321</Old_TIN>
<Org_NPI>1234567890</Org_NPI>
<Ind_NPI>1234567890</Ind_NPI>
<CCN>123456</CCN>
<PRG_Eff_Dt>20160101</PRG_Eff_Dt>
<PRG_Term_Dt>20161231</PRG_Term_Dt>
</Participant>
</Participants>
<Trailer>
<TrailerCode>TRL_PFPRVDR</TrailerCode>
<FileCreationDate>20160101</FileCreationDate>
<RecordCount>1</RecordCount>
</Trailer>
</ACOParticipantData>
You need to get fist get list of Participants then fetch all Participants tin number into list like
Here i created console app for your demonstration purpose.
class Program
{
static void Main(string[] args)
{
XDocument doc = XDocument.Load(#"Path to your xml file");
List<long> tinList = new List<long>();
tinList = doc.Descendants("Participants").Elements().Elements("TIN").Select(x => (long)x).ToList();
foreach (long tin in tinList)
{
Console.WriteLine(tin);
}
Console.ReadLine();
}
}
Output: (For 2 Participants)
You need to create a class for each node and use XML deserialisation to create the object.
I had to remove the empty namespace as the deserialisation process requires a valid namespace.
Also you can change the type of the properties according to your needs.
using System;
using System.IO;
using System.Xml.Serialization;
using System.Linq;
public class Program
{
public class ACOParticipantData
{
public Header Header { get; set; }
public Participant[] Participants { get; set; }
}
public class Header
{
public string HeaderCode { get; set; }
public string FileCreationDate { get; set; }
public string ACOProgCode { get; set; }
}
public class Participant
{
public string ACO_ID { get; set; }
public string TIN { get; set; }
public string Old_TIN { get; set; }
public string Org_NPI { get; set; }
public string Ind_NPI { get; set; }
public string CCN { get; set; }
public string PRG_Eff_Dt { get; set; }
public string PRG_Term_Dt { get; set; }
}
public class Trailer
{
public string TrailerCode { get; set; }
public string FileCreationDate { get; set; }
public string RecordCount { get; set; }
}
public static void Main()
{
var xmlString = #"<ACOParticipantData>
<Header>
<HeaderCode>HDR_PFPRVDR</HeaderCode>
<FileCreationDate>20160101</FileCreationDate>
<ACOProgCode>21</ACOProgCode>
</Header>
<Participants>
<Participant>
<ACO_ID>V199</ACO_ID>
<TIN>123456789</TIN>
<Old_TIN>987654321</Old_TIN>
<Org_NPI>1234567890</Org_NPI>
<Ind_NPI>1234567890</Ind_NPI>
<CCN>123456</CCN>
<PRG_Eff_Dt>20160101</PRG_Eff_Dt>
<PRG_Term_Dt>20161231</PRG_Term_Dt>
</Participant>
<Participant>
<ACO_ID>V199</ACO_ID>
<TIN>123456780</TIN>
<Old_TIN>987654321</Old_TIN>
<Org_NPI>1234567890</Org_NPI>
<Ind_NPI>1234567890</Ind_NPI>
<CCN>123456</CCN>
<PRG_Eff_Dt>20160101</PRG_Eff_Dt>
<PRG_Term_Dt>20161231</PRG_Term_Dt>
</Participant>
</Participants>
<Trailer>
<TrailerCode>TRL_PFPRVDR</TrailerCode>
<FileCreationDate>20160101</FileCreationDate>
<RecordCount>1</RecordCount>
</Trailer>
</ACOParticipantData>";
var serializer = new XmlSerializer(typeof(ACOParticipantData));
ACOParticipantData obj = null;
using (var reader = new StringReader(xmlString))
{
obj = (ACOParticipantData)serializer.Deserialize(reader);
}
if (obj == null)
{
return;
}
foreach (var tin in obj.Participants.Select(x => x.TIN))
{
Console.WriteLine(tin);
}
}
}
Output:
123456789
123456780

C# Custom Property Attribute Reflection

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");

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");

I have a c# class that returns some fields....but I want it to return a list of custom objects (I think!!!)

This is a very noob question, but I have been googling and can't seem to work out the solution myself.
I have created a class that has a number of fields (below). I am grabbing data from a .JSON file.
public class WeatherData
{
//WeatherDatas
public string airtemp { get; set; }
public string apparenttemp { get; set; }
public string windspeedkph { get; set; }
public string windgustskph { get; set; }
public string humidity { get; set; }
public string dewpoint { get; set; }
public string deltaT { get; set; }
public string pressure { get; set; }
public WeatherData(string json, int index)
{
JObject jsonObject = JObject.Parse(json);
JToken jObbs = jsonObject["observations"];
JToken jData = jObbs["data"];
airtemp = (string)jData[index]["air_temp"];
apparenttemp = (string)jData[index]["apparent_t"];
windspeedkph = (string)jData[index]["wind_spd_kmh"];
windgustskph = (string)jData[index]["gust_kmh"];
humidity = (string)jData[index]["rel_hum"];
pressure = (string)jData[index]["press_qnh"];
}
}
Using the above I can get the "airtemp" from "WeatherData.airtemp". But due to some bells and whistles I want to add later what I really want to do is return not just the airtemp value but a field/property indicating the type of the value. For example something like:
WeatherData.WeatherDatas.airtemp.value & WeatherData.WeatherDatas.airtemp.type
Where .value would be air temp and .type be the string "airtemp".
I just can't seem to work out how to describe to google what I am trying to do.
I think you may want to look into using a dictionary. Dictionaries in c# use Key, Value pairs, so you may wish to create a dictionary of Your key values would be the type, such as airtemp and the value values would be the value such as 32.54.
I recommend looking up this page if you're new to C# and want to learn Dictionaries, or any other cool C# things. http://www.dotnetperls.com/dictionary
What you can do here is to use a struct to represent your instance attributes. S.t like this:
public struct Data
{
public Type type; // if you want a string type (I don't know why) you can use a string type
public string Value;
}
Then you class will look like this:
public class WeatherData
{
//WeatherDatas
public Data airtemp { get; set; }
...
public WeatherData(string json, int index)
{
JObject jsonObject = JObject.Parse(json);
JToken jObbs = jsonObject["observations"];
JToken jData = jObbs["data"];
airtemp.Value = new Data((string)jData[index]["air_temp"], typeof(string));
...
}
}
Note that: If you have too many properties in your class, consider using a class istead of struct.
You need to explore types:
Type myType;
Object windy = "strong";
myType = windy.GetType();
Then use typeof() to see what you've got.
Not sure that dictionary is the way forward.
You can get property name without change your structure:
by example: PropertyUtil<WeatherData>.GetName(x => x.airtemp);
https://stackoverflow.com/a/6043028/440030
(source code)
public static class PropertyUtil<TSource>
{
public static string GetPropertyName<TResult>(
Expression<Func<TSource, TResult>> propertyExpression)
{
return (propertyExpression.Body as MemberExpression).Member.Name;
}
}
You can create your own data type, instead of using string use something else Like;
public class UserString
{
public Type _type{ get; set; }
public string value{ get; set; }
public UserString()
{
_type = null;
value = string.Empty;
}
}
public class WeatherData
{
//WeatherDatas
public UserString airtemp { get; set; }
public UserString apparenttemp { get; set; }
public UserString windspeedkph { get; set; }
public UserString windgustskph { get; set; }
public UserString humidity { get; set; }
public UserString dewpoint { get; set; }
public UserString deltaT { get; set; }
public UserString pressure { get; set; }
public WeatherData(string json, int index)
{
JObject jsonObject = JObject.Parse(json);
JToken jObbs = jsonObject["observations"];
JToken jData = jObbs["data"];
airtemp.value = (string)jData[index]["air_temp"];
// airtemp._type = typeof(string); //Something like that.
apparenttemp = (string)jData[index]["apparent_t"];
windspeedkph = (string)jData[index]["wind_spd_kmh"];
windgustskph = (string)jData[index]["gust_kmh"];
humidity = (string)jData[index]["rel_hum"];
pressure = (string)jData[index]["press_qnh"];
}
}

Categories

Resources