Get all value of attribute from each Element from XML - c#

I have an xml file which looks like this:
<HeadercardUnit EndTime="2065-25-45 20:32:44" StartTime="2065-25-45 20:32:23" Rejects="NO" MilliSec="1" Currency="USD" DeclaredDepositAmount="0" denomvalue="1" DepositID="" CustomerID="" HeaderCardID="">
<Counter Number="2" Currency="USD" Output="Stacked" Quality="Accepted" Issue="2006" Value="5" DenomID="" DenomName="5 USD-2006"/>
<Counter Number="31" Currency="USD" Output="Stacked" Quality="Accepted" Issue="2000" Value="1" DenomID="" DenomName="1 USD-2000"/>
<Sum Number="33" Currency="USD" Output="Stacked" Sum="41.00"/>
</HeadercardUnit>
I try to parse it with this code:
string[] content = Directory.GetFiles(Directory.GetCurrentDirectory() + #"\", "*.xml");
XDocument xdoc = XDocument.Load(content[0]);
XElement xml1 = XElement.Load(content[0]);
string xml2 = xml1.ToString();
//Console.WriteLine(xml2);
XElement xml = XElement.Parse(xml2);
var counter = xdoc.Descendants("Counter").Count();
var data = from bps in xdoc.Root.Descendants("Machine")
let Param = bps.Element("ParameterSection")
let Opt = Param?.Element("Operator")
let Hcl = Param?.Element("HeadercardUnit")
let Count = Hcl?.Element("Counter")
select new
{
Type = (string)bps.Attribute("Type"),
SerialNum = (string)bps.Attribute("SerialNumber"),
Startime = (string)Param?.Attribute("StartTime"),
Endtime = (string)Param?.Attribute("EndTime"),
Opt = (string)Opt?.Value,
Number = (string)Count?.Attribute("Number")
};
foreach (var pcl in data)
{
MessageBox.Show(counter.ToString());
for (int i = 0; i < counter; i++)
{
LogService(string.Format("{0},{1},{2},{3},{4},{5}",
pcl.Type, pcl.SerialNum, pcl.Startime, pcl.Endtime, pcl.Opt, pcl.Number));
}
}
The result only give me one line which is looping two time because the counter tag have two elements looks like this:
BPSC1,309322,2065-25-45 20:32:23,2065-25-45 20:32:44,User1,2
BPSC1,309322,2065-25-45 20:32:23,2065-25-45 20:32:44,User1,2

It's a little hard to give a definite answer given you've omitted a portion of the XML that would allow this to be reproduced. However, this line:
let Count = Hcl?.Element("Counter")
Gets the first Counter element. If you want all of them (as you suggest), then you need to iterate through those:
from Count in Hcl.Elements("Counter")
This will then create an object in data for each Counter element.

i added the line inspired by #Charles Mager
from bps in xdoc.Root.Descendants("Machine")
from Countx in bps.Descendants("Counter")
then i can call all attributes

Related

Parse data from XML

I am struggling to read out the "ProtectedElements" node of the xml file below.
I can read out the information above that node without an issue, but I cant seem to find a way to read out the checksum from that particular node.
When i try to read it, the code returns "null".
I have tried to look up the documentation from Microsoft but the examples dont seem to help. I guess it is something to do with the nesting but I cant figure out a solution.
I only want to read out the value of the attribute.
Any help appreciated.
var doc = XDocument.Load(TempFile);
// get LockInfo element
var lockInfoElement = doc.Root.Element(ns + "LockInfo");
// get lockDate
var lockDateAttr = lockInfoElement.Attribute("lockDate");
var lockDateText = lockDateAttr.Value;
var lockDate = DateTime.Parse(lockDateText);
// get locked by
var lockedByAttr = lockInfoElement.Attribute("lockedBy");
var lockedByText = lockedByAttr.Value;
var lockedBy = lockedByText.ToString();
// get ValidationInfo element
var validationInfoElement = doc.Root.Element(ns + "ValidationInfo");
var validationDateAttr = validationInfoElement.Attribute("validationDate");
var validationDateText = validationDateAttr.Value;
var validationDate = DateTime.Parse(validationDateText);
// get ValidationInfo element
var validationByAttr = validationInfoElement.Attribute("validatedBy");
var validationByText = validationByAttr.Value;
var validationBy = validationByText.ToString();
// get ConfigSeal checksum element
var configInfoElement = doc.Root.Element(ns + "ConfigurationSeal");
var configurationSealAttr = configInfoElement.Attribute("checksum");
var configurationSealChkSum = configurationSealAttr.Value;
var configurationSealText = configurationSealChkSum.ToString();
//--------------------------------------------------------------------
// get Protected elements checksum
var protectedElements = doc.Root.Element(ns + "ProtectedElements");
var protectedElementsAttr = protectedElements.Attribute("checksum");
var protectedElementsChkSum = protectedElementsAttr.Value;
var protectedElementsText = protectedElementsChkSum.ToString();
XML DATA:
<SafetyConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:abb-robotics-safety-controller-configuration sc_cfg.1.00.01.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" version="1.00.01" xmlns="urn:abb-robotics-safety-controller-configuration">
<ValidationInfo validationDate="2019-08-29T13:45:16" validatedBy="W01 Admin"/>
<LockInfo lockDate="2020-02-11T13:54:07" lockedBy="W01 Admin" controllerId="6700-110323"/>
<ConfigurationSeal checksum="776890E3E367E5171CC3FD52700BE8FA5373422BCC6C4214C0321E6BEC02EA52" creationDate="2019-08-29T13:39:23.2736126+02:00" createdBy="W01 Admin" systemName="00RZ21_ST010_IR001" swVersion="1.02.04">
<ProtectedElements checksum="20E318119904F2A8506AD3A00035BE2FE562FA772D371DCC43B04A2E4622550F" />
<SiosCfg version="1.0">
<Net name="PNFDevice">
<Device name="PNFDeviceDev" inSizeBits="64" outSizeBits="64" address="">
<Module name="InputOutputModule" inputSize="8" outputSize="8" sourceAddress="100" destAddress="144" wdTimeout="500" crcLength="3" fParVersion="2" slot="3" silLevel="2" visible="true" readonly="false" />
var protectedElements = configInfoElement.Element(ns + "ProtectedElements");
var protectedElementsAttr = protectedElements.Attribute("checksum");
On the first line we're looking under configInfoElement rather than doc.Root, and in the second line we're looking under protectedElements rather than configInfoElement

XElement.Elements() returning empty collection

I'm trying to get the values from an XElement that I'm receiving from db notification.
The xml structure is like this:
<?xml version="1.0"?>
<root>
<inserted>
<row>
<CODI_AVERIA>22</CODI_AVERIA>
<NUMERO_LINIA>2</NUMERO_LINIA>
<DIA>2016-07-17T00:00:00</DIA>
<HORA>1899-12-30T10:26:15.790</HORA>
<CODI_USUARI>1</CODI_USUARI>
<ACCIO>0</ACCIO>
<CODI_PSEUDO>-1</CODI_PSEUDO>
</row>
</inserted>
</root>
And this is the method that I'm using to get the data, and it returns me a List that is empty.
static void getAccio(XElement xml)
{
try
{
xml.Descendants("deleted").Remove();
var items = xml.Elements("row").Select(n => new
{
Codi_averia = n.Element("CODI_AVERIA").Value,
Numero_linia = n.Element("NUMERO_LINIA)").Value,
Accio = n.Element("ACCIO").Value
}).ToList();
}
catch (Exception e)
{
Console.Write(e.Message);
}
}
I have tried to get the value of each field apart and it doesn't allow me to get them separetly as XElements.
Use .Descendants() instead of .Elements():
var items = xml.Descendants("row").Select(n => new
{
Codi_averia = n.Element("CODI_AVERIA").Value,
Numero_linia = n.Element("NUMERO_LINIA)").Value,
Accio = n.Element("ACCIO").Value
}).ToList();
.Elements finds only those elements that are direct descendents, i.e. immediate children. - And I assume that your XElement is the parent of the inserted section.
For difference between .Element and .Descendants see this question
If you don't want to find them in all inner elements too then do .Element("inserted").Elements("rows")
Your document doesn't have any root element with the name row. Instead, you need to select inserted first, and enumerate the row elements of inserted:
var xml = #"<inserted>
<row>
<CODI_AVERIA>21</CODI_AVERIA>
<NUMERO_LINIA>2</NUMERO_LINIA>
<DIA>2016-07-17T00:00:00</DIA>
<HORA>1899-12-30T10:26:15.790</HORA>
<CODI_USUARI>1</CODI_USUARI>
<ACCIO>0</ACCIO>
<CODI_PSEUDO>-1</CODI_PSEUDO>
</row>
</inserted>";
var xdoc = XDocument.Parse(xml);
var items = xdoc.Element("inserted").Elements("row").Select(n => new
{
Codi_averia = n.Element("CODI_AVERIA").Value,
Numero_linia = n.Element("NUMERO_LINIA").Value,
Accio = n.Element("ACCIO").Value
}).ToList();
In your sample code ("NUMERO_LINIA)") is wrong because of additionnal quote and parenthesis.
This works for me :
XDocument xd = XDocument.Load("XMLFile1.xml");
var lst = (from n in xd.Descendants("row")
select new
{
Codi_averia = n.Element("CODI_AVERIA").Value,
Numero_linia = n.Element("NUMERO_LINIA").Value,
Accio = n.Element("ACCIO").Value
}).ToList();
foreach (var item in lst)
Console.WriteLine(string.Format("{0}{1}{2}", item.Codi_averia, item.Numero_linia, item.Accio));

how to convert csv to xml with different headers

I have a csv with different column headers and I want to convert this to an XML payload.
The csv looks like following.
TEST1,APPLICATION_NAME,START_TIME,STOP_TIME,SERVICE_DESCRIPTION,FILING_STATUS,TIME_OF_LAST_UPDATE,RECORD_STATUS,ERROR_MESSAGE
,,20120101000000ES,20140131000000ES,New FGH Application,,,
,,20140304000000ES,20161231000000ES,New FGH Application,,,
,,20150109000000ES,20201231000000ES,New FGH Application,,,
TEST2,app,TOL,QUEUED
,nits,20120101000000ES,20201231000000ES
I tried to do this with Linq but couldn't figure out a way. Also I don't really want to specify columns like in the following example.
https://msdn.microsoft.com/en-us/library/bb387090
please note that this csv has different column headers.
The output I am expecting is;
<Root>
<TEST1>
<APPLICATION_NAME></APPLICATION_NAME>
<START_TIME>20120101000000ES</START_TIME>
<STOP_TIME>20140131000000ES</STOP_TIME>
<SERVICE_DESCRIPTION>New NITS Application</SERVICE_DESCRIPTION>
<FILING_STATUS></FILING_STATUS>
<TIME_OF_LAST_UPDATE></TIME_OF_LAST_UPDATE>
<RECORD_STATUS></RECORD_STATUS>
</TEST1>
<TEST1>
<APPLICATION_NAME></APPLICATION_NAME>
<START_TIME>20140304000000ES</START_TIME>
<STOP_TIME>20161231000000ES</STOP_TIME>
<SERVICE_DESCRIPTION>New NITS Application</SERVICE_DESCRIPTION>
<FILING_STATUS></FILING_STATUS>
<TIME_OF_LAST_UPDATE></TIME_OF_LAST_UPDATE>
<RECORD_STATUS></RECORD_STATUS>
</TEST1>
<TEST1>
<APPLICATION_NAME></APPLICATION_NAME>
<START_TIME>20150109000000ES</START_TIME>
<STOP_TIME>20201231000000ES</STOP_TIME>
<SERVICE_DESCRIPTION>New NITS Application</SERVICE_DESCRIPTION>
<FILING_STATUS></FILING_STATUS>
<TIME_OF_LAST_UPDATE></TIME_OF_LAST_UPDATE>
<RECORD_STATUS></RECORD_STATUS>
</TEST1>
<TEST2>
<app>nits</app>
<TOL>20120101000000ES</TOL>
<QUEUED>20201231000000ES</QUEUED>
</TEST2>
</root>
Thanks for your help.
update: this is what I started off with.
string[] headers = lines[0].Split(',').Select(x => x.Trim('\"')).ToArray();
var xml = new XElement("root",
lines.Where((line, index) => index > 0).Select(line => new XElement("TEST",
line.Split(',').Select((column, index) => new XElement(headers[index], column)))));
Expanding on the linked example, you can do this
string[] source = File.ReadAllLines("text.csv");
string IGNORE_ROW = "XXXXX";
List<string> data = new List<string>();
string test = "";
for (int i = 0; i < source.Length; i++)
{
string[] _str = source[i].Split(',');
if (String.IsNullOrWhiteSpace(_str[0])) _str[0] = test;
else
{
test = _str[0];
_str[0] = IGNORE_ROW;
}
source[i] = String.Join(",", _str);
}
XElement data = new XElement("Root",
from str in source
where str.StartsWith(IGNORE_ROW) == false
let fields = str.Split(',')
select new XElement(fields[0],
new XElement("APPLICATION_NAME", fields[1]),
new XElement("START_TIME", fields[2]),
new XElement("STOP_TIME", fields[3]),
new XElement("SERVICE_DESCRIPTION", fields[4]),
new XElement("FILING_STATUS", fields[5]),
new XElement("TIME_OF_LAST_UPDATE", fields[6]),
new XElement("RECORD_STATUS", fields[7])
)
);
Console.WriteLine(data);
It is simply a matter for renaming the relevant elements and including them in the correct order.
// Edited
After reviewing the comment, it appears you are repeating the header within the data so that it can used as an element name. If you have control over the csv generation, remove this repeated row, and simply output the test value as the first element in the csv.
If you do not have control over the csv, you can alter the text so that it can be set. This is what the edited example does.
Use TextFieldParser to read the csv file and parse it into classes.
Then use XDocument to build a xml document in memory and write it to a file after its completed.

How to extract xml child element

I am trying to figure out the code to extract xml child (I think this is worded correctly) elements. I have searched and tried many samples but cannot find how to drill down to pick out the section I want and return the information I need. Maybe I all I need is someone to define the data I am trying to pull so I can read up on the issue, of course any code would be very helpful and I will figure it out from there. Thanks in advanced for any help!
Here is the xml file. I am trying to run an if statement to find the section named <STATISTICTYPE>PVCAP_CharactersSaved</STATISTICTYPE> and return the <JOBNAME>,<TIMEDELTA>,<VALUESUM>.
<?xml version="1.0" encoding="utf-8"?>
<PVCAPTURESTATISTICCONTAINTER xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<PVCAPTUREJOBSTATISTICS>
<PVCAPTURESTATISTICSUMMARY>
<STATISTICTYPE>PVCAP_CharactersSaved</STATISTICTYPE>
<STATISTICNAME>Characters saved</STATISTICNAME>
<JOBID>24</JOBID>
<JOBNAME>HEAT FILES</JOBNAME>
<TIMEDELTA>422</TIMEDELTA>
<VALUESUM>25432</VALUESUM>
</PVCAPTURESTATISTICSUMMARY>
<PVCAPTURESTATISTICSUMMARY>
<STATISTICTYPE>PVCAP_CharactersSaved_NoMM</STATISTICTYPE>
<STATISTICNAME>Characters saved (no match and merge)</STATISTICNAME>
<JOBID>24</JOBID>
<JOBNAME>HEAT FILES</JOBNAME>
<TIMEDELTA>422</TIMEDELTA>
<VALUESUM>25432</VALUESUM>
</PVCAPTURESTATISTICSUMMARY>
</PVCAPTUREJOBSTATISTICS>
<DOCUMENTCOUNT>762</DOCUMENTCOUNT>
<PAGECOUNT>3194</PAGECOUNT>
<IMAGECOUNT>3194</IMAGECOUNT>
<VERSION>2.0</VERSION>
</PVCAPTURESTATISTICCONTAINTER>
You can use LINQ to XML, particularly the XElement class.
var element = XElement.Parse(xmlStr).Element("PVCAPTUREJOBSTATISTICS")
.Elements("PVCAPTURESTATISTICSUMMARY")
.First(c => c.Element("STATISTICTYPE").Value == "PVCAP_CharactersSaved")
var jobName = element.Element("JOBNAME").Value;
var timeDelta = element.Element("TIMEDELTA").Value;
var valueSum = element.Element("VALUESUM").Value;
You'll want to add in some error handling and whatnot here, but this should get you going in the right direction.
You can do something like this:
XElement res = XElement.Parse(xmlResult);
foreach(var elem in res.Element("PVCAPTUREJOBSTATISTICS").Elements("PVCAPTURESTATISTICSUMMARY"))
{
if (elem.Element("STATISTICTYPE").Value.Equals("PVCAP_CharactersSaved", StringComparison.Ordinal))
{
string jobName = elem.Element("JOBNAME").Value;
string timeDelta = elem.Element("TIMEDELTA").Value;
string valueSum = elem.Element("VALUESUM").Value;
}
}
You can use XDocument and LINQ-to-XML to do that quite easily, for example :
string xml = "your xml content here";
XDocument doc = XDocument.Parse(xml);
//or if you have the xml file instead :
//XDocument doc = XDocument.Load("path_to_xml_file.xml");
var result = doc.Descendants("PVCAPTURESTATISTICSUMMARY")
.Where(o => (string) o.Element("STATISTICTYPE") == "PVCAP_CharactersSaved")
.Select(o => new
{
jobname = (string) o.Element("JOBNAME"),
timedelta = (string) o.Element("TIMEDELTA"),
valuesum = (string) o.Element("VALUESUM")
});
foreach (var r in result)
{
Console.WriteLine(r);
}

Retrieving element attributes for multiple nodes

I am having the hardest time figuring this out, I have a XML doc that has multiple nodes with the same name. Within those nodes are even more nodes with same name but different attributes and thats what I want to capture. Here is an example of the XML:
<?xml version="1.0" encoding="utf-8"?>
<TopologyDefinition xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/online/managementsystems/topologydefinition/2009/11">
<Topology Name="testenv">
<DataCenters>
<DataCenter Name="FL" Type="Active">
<Machines>
<Machine Name="FLVMServer1" VmHost="FLVHost100">
<IPBindings>
<IPBinding VirtualNetworkType="Data" IP="192.168.0.10" />
</IPBindings>
</Machine>
<Machine Name="FLVMServer2" VmHost="FLVHost200">
<IPBindings>
<IPBinding VirtualNetworkType="Data" IP="192.168.0.20" />
</IPBindings>
</Machine>
</DataCenter>
<DataCenter Name="RI" Type="Passive">
<Machines>
<Machine Name="RIVMServer1" VmHost="RIVHost100">
<IPBindings>
<IPBinding VirtualNetworkType="Data" IP="192.168.2.10" />
</IPBindings>
</Machine>
<Machine Name="RIVMServer2" VmHost="RIVHost200">
<IPBindings>
<IPBinding VirtualNetworkType="Data" IP="192.168.2.20" />
</IPBindings>
</Machine>
</DataCenter>
</DataCenters>
</Topology>
</TopologyDefinition>
I need to capture for all DC's the following:
Machine Name
VmHost
IP
I've tried XPATH, I've tried iterating through each node as well with no luck
ServerInfoClass serverInfo = new ServerInfoClass();
XmlDocument doc = new XmlDocument();
doc.Load(FilePath);
XmlNodeList dcElemList = doc.GetElementsByTagName("DataCenter");
for(int j = 0; j < dcElemList.Count; j++)
{
XmlNodeList elemList = doc.GetElementsByTagName("Machine");
for (int i = 0; i < elemList.Count; i++)
{
serverInfo.ServerName = elemList[i].Attributes["Name"].Value;
serverInfo.VmHost = elemList[i].Attributes["VmHost"].Value;
XmlNodeList ipList = doc.GetElementsByTagName("IPBindings");
for (int x = 0; x < ipList.Count; x++) ;
{
//serverInfo.IPAddress = ipList[x].Attributes["IP"].Value;
}
OutPut(serverInfo.ServerName, serverInfo.VmHost, serverInfo.IPAddress);
}
}
If you put that into an XDocument, you can query it like this:
XDocument document = // ... your document.
var ns = document.Root.Name.Namespace;
var results = from dcNode in document.Descendants(ns + "DataCenter")
let Name = dcNode.Attribute("Name").Value
let Type = dcNode.Attribute("Type").Value
let Machines = dcNode.Descendants(ns + "Machine").Select(mNode =>
new {
Name = mNode.Attribute("Name").Value,
VmHost = mNode.Attribute("VmHost").Value,
Bindings = mNode.Descendants(ns + "IPBinding").Attributes("IP").Select(x => x.Value).ToArray()
})
select new { Name, Type, Machines };
I'd recommend making a helper extension method for getting attribute values, which also checks for nulls.
One of the keys here is to remember that your XML elements live in a namespace, and you need to supply the full namespace when querying. That is why it is handy here to extract the ns namespace instance from the root node at first.
Your example isn't well-formed. You're missing the </Machines> tags.
Another way to get do it is using LINQ to XML:
XNamespace ns = "http://schemas.microsoft.com/online/managementsystems/topologydefinition/2009/11";
foreach (var machine in XElement.Load(#"c:\mydata.xml").Descendants(ns + "Machine"))
{
string name = machine.Attribute("Name").Value;
string vmHost = machine.Attribute("VmHost").Value;
XElement ipBinding = machine.Descendants(ns + "IPBinding").Single();
string vnType = ipBinding.Attribute("VirtualNetworkType").Value;
string ip = ipBinding.Attribute("IP").Value;
}

Categories