C# deserialize xml using linq - c#

I have the following xml file
<?xml version="1.0" encoding="utf-8"?>
<Launchpad>
<Shortcuts>
<Shortcut Id="1">
<Type>Folder</Type>
<FullPath>C:\bla\bla\bla</FullPath>
<Name>Proximity</Name>
</Shortcut>
<Shortcut Id="2">
<Type>Folder</Type>
<FullPath>C:\bla</FullPath>
<Name>Visual Studio 2017</Name>
</Shortcut>
</Shortcuts>
</Launchpad>
I am trying to deserialize to an object like this: (Tried query syntax first, didn't work either)
XDocument xd = XDocument.Load(FullPath);
// query syntax
//var shortcuts = (from s in xd.Descendants("Shortcuts")
// select new Shortcut()
// {
// Id = Convert.ToInt32(s.Attribute("Id")),
// TypeOfLink = GetTypeFromString(s.Descendants("Type")
// .First()
// .Value),
// FullPathToTarget = s.Descendants("FullPath")
// .First()
// .Value,
// Name = s.Descendants("Name").First().Value,
// }).ToList();
// method syntax
List<Shortcut> shortcuts = xd.Descendants("Shortcuts")
.Select(s =>
new Shortcut()
{
//Id = Convert.ToInt32(s.Attribute("Id")),
TypeOfLink = GetTypeFromString(s.Descendants("Type")
.First().Value),
FullPathToTarget = s.Descendants("FullPath")
.First().Value,
Name = s.Descendants("Name")
.First().Value,
}).ToList();
return shortcuts;
For some reason I only get a single shortcut object in the list. Also, for some reason, the s.Attribute("Id") is null.
If anyone has any suggestions to improve the linq query or why the attribute is not working it would be a great help

You have to read .Descendants("Shortcut") instead of .Descendants("Shortcuts").
Something like this:
List<Shortcut> shortcuts = xd.Descendants("Shortcut").Select(s =>
new Shortcut()
{
Id = s.Attribute("Id").Value,
TypeOfLink = s.Descendants("Type").First().Value,
FullPathToTarget = s.Descendants("FullPath").First().Value,
Name = s.Descendants("Name").First().Value,
}).ToList();

I get the full list by selecting the Shortcut as a descendant of Shortcuts.
Also, ID is an XAttribute so you cant convert it to int.
You need to use Value to get the attributes value.
XDocument xd = XDocument.Load(ms);
XElement root = xd.Document.Root;
var list = root.Descendants("Shortcuts").Descendants("Shortcut").Select(x =>
new Shortcut()
{
Id = Convert.ToInt32(x.Attribute("Id").Value),
TypeOfLink = GetTypeFromString(x.Descendants("Type")
.First().Value),
FullPathToTarget = x.Descendants("FullPath")
.First().Value,
Name = x.Descendants("Name").First().Value
}).ToList();

Related

C# reading xml inside certain node

I need help. I'm trying to figure out a way to read inside a bracket like this one:
<group id = "56">
<name>Counter</name>
</group>
In the code, there are mulitiple places where the same pattern comes back, and I would like to get all the group id number's and their name.
This is my code:
XDocument doc = XDocument.Parse(_XmlFile);
var results = doc.Descendants("group").Select(x => new
{
id = (int)x.Attribute("id"),
name = x.Attribute("name").Value,
}).ToList();
Console.WriteLine(results);
Thanks
Your code looks quite OK, but name is an element and not an attribute, so it should be
XDocument doc = XDocument.Parse(_XmlFile);
var results = doc.Descendants("group").Select(x => new
{
id = (int)x.Attribute("id"),
name = (string)x.Element("name"),
}).ToList();
foreach (var x in results)
Console.WriteLine("id: {0} name: {1}", x.id, x.name);
Use GetElementsByTagName method.
Here is the microsoft article explaining it with examples.
https://msdn.microsoft.com/en-us/library/dc0c9ekk(v=vs.110).aspx
"Name" is not an attribute, but a child node. The solution is something like this:
XDocument doc = XDocument.Parse(_XmlFile);
var results = doc.Descendants("group").Select(x => new
{
id = int.Parse(x.Attribute("id").Value),
name = x.Descendants("name").First().Value
}).ToList();

XML - C# Generic method for getting a Value using a Key on a Serialized Object

I have an xml file that looks like below. And I want to create a method that would get me the value using the key.
</cbn:Class>
<cbt:Students>
<cbt:Student>
<cbt:Key>x-param-key-studentName-1</cbt:Key>
<cbt:Value>x-param-studentSurname-val</cbt:Value>
</cbt:Student>
<cbt:Student>
<cbt:Key>x-param-key-studentName-2</cbt:Key>
<cbt:Value>x-param- studentSurname-val</cbt:Value>
</cbt:Student>
</cbt:Students>
</cbn:Class>
So, i want to get the valud of studentSurname using as key the studentName.
The xml is serialized, so I can access for example the Student as an object.
I can get the value of the first student by using the code I have written as below:
string studentSurname = myData.Class.Students.Student[0].Value;
Where myData is the serialized object. I would like a more generic method instead of getting just the first student.
EDIT: Another XML
<?xml version="1.0" encoding="UTF-8"?>
<cbn:PaidOrderNotification xmlns:cbn="http://*/3.12.0.0/*.xsd">
<cbn:NotificationDate>2016-07-29T11:59:29.1137865Z</cbn:NotificationDate>
<cbn:Purchase cbt:Id="95233035" xmlns:cbt="http://xml.*.com/3.12.0.0/*.xsd">
<cbt:Status>Test Order</cbt:Status>
<cbt:StatusId>TST</cbt:StatusId>
<cbt:Items>
<cbt:Item cbt:RunningNo="1">
<cbt:ProductId>175358</cbt:ProductId>
<cbt:ProductReportingGroup>Basic alle Laufzeiten</cbt:ProductReportingGroup>
<cbt:YourCurrencyId>EUR</cbt:YourCurrencyId>
<cbt:ProfitCalculation>
<cbt:GrossRevenue>566.44</cbt:GrossRevenue>
</cbt:ProfitCalculation>
<cbt:YourPrice>
<cbt:TotalTotalPrice>
</cbt:TotalTotalPrice>
</cbt:YourPrice>
<cbt:Deliveries />
<cbt:Additionals />
<cbt:ExtraParameters />
</cbt:Item>
</cbt:Items>
<cbt:ExtraParameters>
<cbt:ExtraParameter>
<cbt:Key>x-my-key</cbt:Key>
<cbt:Value> x-my-val</cbt:Value>
</cbt:ExtraParameter>
</cbt:ExtraParameters>
</cbn:Purchase>
</cbn:PaidOrderNotification>
I would like to take from here this value:
<cbt:ExtraParameters>
<cbt:ExtraParameter>
<cbt:Key>x-didi</cbt:Key>
<cbt:Value>sfsfd</cbt:Value>
</cbt:ExtraParameter>
</cbt:ExtraParameters>
How can i do that?
Your XML document have namespaces but your example do not includes it, do I had to fake some values.
Once you have the URI for the namespaces, you can use a XmlNamespaceManager to look into the document with the appropriate qualifier
var xml = #"
<cbn:Class xmlns:cbn='something' xmlns:cbt='something-else'>
<cbt:StudentNr></cbt:StudentNr>
<cbt:Students>
<cbt:Student>
<cbt:Key>x-param-key-studentName-1</cbt:Key>
<cbt:Value>x-param-studentSurname-val</cbt:Value>
</cbt:Student>
<cbt:Student>
<cbt:Key>x-param-key-studentName-2</cbt:Key>
<cbt:Value>x-param- studentSurname-val</cbt:Value>
</cbt:Student>
</cbt:Students>
</cbn:Class>";
var doc = new XmlDocument();
doc.LoadXml(xml);
var students = doc.SelectSingleNode("//*[local-name() = 'Students']");
var nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("cbt", students.NamespaceURI);
var dictionary = students.SelectNodes("cbt:Student", nsmgr).OfType<XmlElement>()
.ToDictionary(student => student.SelectSingleNode("cbt:Key" , nsmgr).InnerText,
student => student.SelectSingleNode("cbt:Value", nsmgr).InnerText);
EDIT: You can do this as well:
string studentSurname = "";
var dictionary = myData.Class.Students.ToDictionary(student => student.Key,
student => student.Value);
var exists = dictionary.TryGetValue("x-param-key-studentName-1", out studentSurname);
EDIT2: Note you have two different cbt:ExtraParameters elements, and the first one is empty. I extended the code to work with any cbt:ExtraParameter elements
var doc = new XmlDocument();
doc.LoadXml(xml);
var dictionary = doc.SelectNodes("//*[local-name() = 'ExtraParameter']")
.OfType<XmlElement>()
.ToDictionary(
extra => extra.SelectSingleNode("*[local-name() = 'Key' ]").InnerText,
extra => extra.SelectSingleNode("*[local-name() = 'Value']").InnerText
);
var studentSurname = "";
var exists = dictionary.TryGetValue("x-my-key", out studentSurname);

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

Linq to XML dynamic XML Decendants

I'm parsing a lot of XML files using Linq to XML synatx, everything works when I try to access top level elements
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot")
select new
{
model = (string)element.Element("MODEL"),
}).FirstOrDefault()
The problem occurs when I need to access lower level childs of that document I tried:
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot")
select new
{
ddName = (string)element.Descendants("DD_NAME").Elements("name").First();
}).FirstOrDefault()
and
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot").Descendants("DD_NAME")
select new
{
ddName = (string)element.Element("name")
}).FirstOrDefault();
Sadly none of that works and i get same error "Sequence contains no elements". And one more thing sometimes the XML document contains those tags and sometimes not is something like this enough for handling this case?
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot").Descendants("DD_NAME")
select new
{
ddName = (string)element.Element("name") ?? "-"
}).FirstOrDefault();
Edit:
I don't think is possible to paste short version of XML that would be simple, so here's full version: http://pastebin.com/uDkP3rnR and for the code example:
XDocument prodcutDocument = XDocument.Load(this.ServerPATHData + file);
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot")
select new
{
modelis = (string)element.Element("MODELIS"),
T2918_0 = (string)element.Descendants("dd_DARBINIS_GRAFIKAS_SPEC").First()
}).FirstOrDefault();
writeTxt.WriteLine("modelis: " + indexroot.modelis);
writeTxt.WriteLine("T2979_0" + indexroot.T2918_0);
In examining the sample XML that you posted on PasteBin, it appears to me that the elements that you mention appear only once. To access them, you can simply specify a path to each as follows:
XElement indexroot = document.Root.Element("indexroot");
XElement modelis = indexroot.Element("MODELIS");
XElement dd_dgs = indexroot.Element("dd_DARBINIS_GRAFIKAS_SPEC");
XElement voltageuv = dd_dgs.Element("VoltageUV");
string t2979_0 = (string)voltageuv.Element("T2979_0");
string t2861_60 = (string)voltageuv.Element("T2861_60");
string t2757_121 = (string)voltageuv.Element("T2757_121");
(Note that you may need to check for null if there is a chance that any of the elements you are trying to access may not be present. Without doing so, you'll encounter a NullReferenceException.)
Here is a snippet of the XML that you posted to give context to the above code:
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<PDB>
<indexroot>
<ed_BENDRA_MAKS_SUV_GALIA>1.45</ed_BENDRA_MAKS_SUV_GALIA>
<ed_BENDRA_MAKS_SROVE>6.48</ed_BENDRA_MAKS_SROVE>
<TIPAS>1</TIPAS>
<MODELIS>RIS 2500 HW EC 3.0</MODELIS>
<dd_DARBINIS_GRAFIKAS_SPEC>
<VoltageUV>
<T2979_0>229,42</T2979_0>
<T2861_60>227,98</T2861_60>
<T2757_121>228,97</T2757_121>
</VoltageUV>
<CurrentIA>
<T2979_0>2,56</T2979_0>
<T2861_60>2,63</T2861_60>
<T2757_121>2,72</T2757_121>
</CurrentIA>
</dd_DARBINIS_GRAFIKAS_SPEC>
</indexroot>
</PDB>
You can just change:
element.Descendants("dd_DARBINIS_GRAFIKAS_SPEC").First()
to this:
element.Descendants("dd_DARBINIS_GRAFIKAS_SPEC").FirstOrDefault() ?? "-"

Categories