<List> to XML with Linq c# - c#

I've already parsed a horrible document into a list, but I'm having issues formatting it with Linq and cant seem to get it quite right.
My code looks like this:
var xEle = new XElement("WorkOrder",
from item in items
select new XElement(item.SECTION,
new XElement(item.DATANAME, item.DATAVALUE)));
Which produces something like this:
<WorkOrder>
<JOBDETAILS>
<JOBNUMBER>12345</JOBNUMBER>
</JOBDETAILS>
<JOBDETAILS>
<ACTIVITYNO>/01</ACTIVITYNO>
</JOBDETAILS>
<JOBDETAILS>
<JOBCCD>15/06/2015
</JOBCCD>
</JOBDETAILS>
<JOBDETAILS>
<REQUIRED>15/06/2016
</REQUIRED>
</JOBDETAILS>
<COMPANYDETAILS>
<COMPANY>Adventure Works
</COMPANY>
</COMPANYDETAILS>
<COMPANYDETAILS>
<STREET>LTD
</STREET>
</COMPANYDETAILS>
<COMPANYDETAILS>
<LOCALITY>123 Street
</LOCALITY>
</COMPANYDETAILS>
<COMPANYDETAILS>
<TOWN>Local Town
</TOWN>
</COMPANYDETAILS>
<COMPANYDETAILS>
<COUNTY>County Name
</COUNTY>
</COMPANYDETAILS>
<COMPANYDETAILS>
<POSTCODE>ABC 1234
</POSTCODE>
<COMMENTS>
<COMMENT>this is a comment</COMMENT>
</COMMENTS>
</WorkOrder>
I'm trying to get it to be grouped by my item.SECTION (in the example output this would be JOBDETAILS & COMPANYDETAILS) which I want to produce something like this:
<WorkOrder>
<JOBDETAILS>
<JOBNUMBER>12345</JOBNUNMBER>
<ACTIVITYNO>/01</ACTIVITYNO>
<JOBCCD>15/06/2015</JOBCCD>
<REQUIRED>15/06/2016</REQUIRED>
</JOBDETAILS>
<COMPANYDETAILS>
<COMPANY>Adventure Works</COMPANY>
<STREET>LTD</STREET>
<LOCALITY>123</LOCALITY>
</COMPANYDETAILS>
<COMMENTS>
<COMMENT>THIS IS A COMMENT</COMMENT>
</COMMENTS>
</WorkOrder>
My list is dynamic and does not contain a specific set of fields each time, hence why I have not hard coded the output.
I dont think I'm a million miles away, and would appreciate a kick in the right direction.

You need to add a groupby to group the results by the section.
var xEle =
new XElement("WorkOrder",
from item in items
group item by item.SECTION into g
select new XElement(g.Key,
g.Select(i => new XElement(i.DATANAME, i.DATAVALUE))));
Note that you also need two selects, one to build up the list of elements and another to wrap them together. (Note that I used function syntax by you could also say a variant of from i in g select)

Related

C# XML Linq on multiple tags of same name

I have some projects in an XML file. eg. Multiple projects like the one below in the same file . I want to search all project entries where FluidTypes matches a particular string .
<?xml version="1.0" encoding="utf-8"?>
<data>
<Project ID="P-2014-000037">
<Name>FDP001_Bakken</Name>
<Manager>shell</Manager>
<Area>NAM</Area>
<Field>Bakken</Field>
<Type>Time and Material External</Type>
<Country>USA</Country>
<Value>3.5</Value>
<Geomarket>NAM</Geomarket>
<FormationTypes>Carbonate</FormationTypes>
<FormationTypes>Coal</FormationTypes>
<FormationTypes>Fractures</FormationTypes>
<FormationTypes>Sandstone</FormationTypes>
<FluidTypes>Gas Cond</FluidTypes>
<FluidTypes>Heavy Oil</FluidTypes>
<DriveMechanisms>Compaction</DriveMechanisms>
<DriveMechanisms>Aquifer</DriveMechanisms>
<EORProcesses>CO2</EORProcesses>
<EORProcesses>CSS</EORProcesses>
</Project>
</data>
I am using the follwing code to search for Geomarket matches :
IEnumerable<XElement> values1 = from el1 in root.Elements("Project").
Where(r => regEx1.IsMatch(r.Element("Geomarket").Value))
select el1;
when I use same for Fluid type (which has multiple elements ):
IEnumerable<XElement> values1 = from el1 in root.Elements("Project").
Where(r => regEx1.IsMatch(r.Element("FluidTypes").Value))
select el1;
It only checks for a match with the first element with name Fluid Types and not ALL fluid types elements . As a result only Gas Cond matches this project but Heavy Oil does not.
How to make a query across all Fluid types search ?
Use a Where clause with a nested search:
var projects = root
.Elements("Project")
.Where(el => el.Elements("FluidTypes").Where(el2 => regEx1.IsMatch(el2.Value)).Any());
This returns all elements named "Project" with at least one nested element named "FluidTypes" whose Value matches your regular expression.
Or, use a nested Any():
var projects = root
.Elements("Project")
.Where(el => el.Elements("FluidTypes").Any(el2 => regEx1.IsMatch(el2.Value)));
Try
IEnumerable<XElement> values1 = from el1 in root.Elements("Project").Elements("FluidTypes")
.Where(r => regEx1.IsMatch(r.Value))
Select el1;

LINQ to XML Query to find all child values where parent element name like X C#

So lets say I have a XElement that looks something like this
<Root>
<ProductOne>
<Size>Large</Size>
<Height>2</Height>
</ProductOne>
<ProductTwo>
<Size>Small</Size>
<Type>Bar</Type>
</ProductOne>
<ProductThree>
<Size>Small</Size>
<Type>Crate</Type>
<Color>Blue</Color>
</ProductOne>
<SomeOtherStuff>
<OtherThing>CrazyData</OtherThing>
</SomeOtherStuff>
</Root>
I want to query this data and get a IEnumerable string of the child values (I.E. Size, Type, Color, and a lot of other possuble attributes) of anything that is in a element with the word "Product" in it.
So My resulting list would look like
Large
2
Small
Bar
Small
Crate
Blue
Could someone tell me how to construct such a query using LINQ?
First, you have a lot of typos with your xml. Here is the correct version:
var xml = #"
<Root>
<ProductOne>
<Size>Large</Size>
<Height>2</Height>
</ProductOne>
<ProductTwo>
<Size>Small</Size>
<Type>Bar</Type>
</ProductTwo>
<ProductThree>
<Size>Small</Size>
<Type>Crate</Type>
<Color>Blue</Color>
</ProductThree>
<SomeOtherStuff>
<OtherThing>CrazyData</OtherThing>
</SomeOtherStuff>
</Root>";
Now, here is some linq magic you can do to get the values you want.
var list = XElement.Parse(xml) //parses the xml as an XElement
.Elements() //gets all elements under the "root" node
.Where(x => x.Name.LocalName.StartsWith("Product")) // only selects elements that
// start with "product"
.SelectMany(x => x.Elements()) // inside of each of the "product" nodes, select
// all the inner nodes and flatten the results
// into a single list
.Select(x => x.Value) //select the node's inner text
.ToList(); //to list (optional)
This will give you back your wanted list as a List<string>.
Large
2
Small
Bar
Small
Crate
Blue

Filter descendants parent [or at least higher level than results]

Still a Linq newbie here, and now having issues with the WHERE clause. I'm trying to return anything found in the printer tags, but only from below the element list type="lff".
If I try to output the descendant elements with no WHERE clause, I get everything (from both <list> elements). When I try to add various versions of a WHERE clause, I get nothing back. I'm obviously not putting the WHERE condition in correctly.
(I need to get the element object, so I can check the NAME and the VALUE. In my example below, I am only outputting the VALUE for now).
Can you advise?
Here is the XML:
<?xml version="1.0"?>
<printerlist>
<list type="aff">
<printserver>print-server1</printserver>
<printserver>print-server2</printserver>
<printserver>print-server3</printserver>
<additionalprinters>
<printer>
<fullname>\\servera\bbb</fullname>
</printer>
</additionalprinters>
</list>
<list type="lff">
<printserver>print-sever4</printserver>
<additionalprinters>
<printer>
<fullname>\\serverb\bbb</fullname>
</printer>
<printer>
<fullname>\\serverc\aaa</fullname>
</printer>
</additionalprinters>
</list>
</printerlist>
And here is the code to try and get the list:
var qq = from c in xml.Descendants("additionalprinters").Descendants("printer")
//where (string) c.Parent.Attribute("type") == "lff"
//Uncommenting the above line means that nothing is returned.
select c;
foreach (XElement q in qq)
{
Console.WriteLine("Test Output: {0}", q.Value );
}
Output is:
Test Output: \\servera\bbb
Test Output: \\serverb\bbb
Test Output: \\serverc\aaa
I am only looking for the final two outputs to be returned, in this particular case.
The parent of printer is additionalprinters and it doesn't have type property, you need to use .Parent twice to get list element.
from c in xml.Descendants("additionalprinters").Descendants("printer")
where (string) c.Parent.Parent.Attribute("type") == "lff"
select c
Or you can also do the following
xml.Descendants("list")
.Where(c => (string) c.Attribute("type") == "lff")
.SelectMany(x => x.Element("additionalprinters").Descendants("printer"))
You can also use XPath selector from System.Xml.XPath namespace for this purpose:
var doc = XDocument.Parse(xml);
var printers = doc.XPathSelectElements("//list[#type='lff']/additionalprinters/printer");

How can I search through xml using linq

I want to search through my xml file. The structure looks like this:
<AForetag xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Place ID="1006">
<Foretag>
<Epost>info#abc123.se</Epost>
<Namn>Abe</Namn>
<Ort>Abc123</Ort>
<Adress>Abc123</Adress>
<Postnummer>Abc123</Postnummer>
<Landskap>Abc123</Landskap>
<Telefon>Abc123</Telefon>
<Medlemskap>Abc123</Medlemskap>
</Foretag>
<Foretag>
<Epost>def456</Epost>
<Namn>def456</Namn>
<Ort>def456</Ort>
<Adress>def456</Adress>
<Postnummer>def456</Postnummer>
<Landskap>def456</Landskap>
<Telefon>def456</Telefon>
<Medlemskap>def456</Medlemskap>
</Foretag>
</Place>
</Aforetag>
And I want to search for the Element <Landskap>. And if I get and match I should pick all the other elements, Epost, Namn, Ort, Adress, Postnummer, Landskap, Telefon and Medlemskap. The info I want to put in an array.
I have tried this:
var aforetag = from foretag in doc.Descendants("Place")
where foretag.Attribute("ID").Value == "1006"
select foretag;
var landskap = aforetag.Elements("Foretag")
.Descendants()
.Where(x => x.Element("Landskap")
.Value
.Contains(s)
.Descendants()
.Select(c => (string)c)
.ToArray();
Your code is not well formed. Copy this into VS and there are a few errors, fix one and more errors!...
And most importantly, your XML is not XML as the start and end tag don't even match! Plus, there are other issues.
Fix all these and I'm sure it will help.
var landskap = aforetag.Elements("Foretag")
.Where(e=>e.Element("Landskap").Value.Contains(s))
.Select(e=>e.Elements().Select(x=>x.Value).ToArray());
//the result is an IEnumerable<string[]> for the matched keyword s

When parsing XML with Linq, only one object gets fetched

I'm trying to populate an array with the following xml:
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<data>
<item>
<date>1307018090</date>
<price>10.4718867</price>
<low>10.38100000</low>
<high>10.49000000</high>
<nicedate>14:39</nicedate>
</item>
<item>
...
</item>
and so on
I'm using this Linq-query, which to me means that It'll create one object per :
var items = from item in doc.Element("data").Descendants()
select new Currency
{
Close = item.Element("price").Value.ToString(),
Date = item.Element("date").Value.ToString(),
Low = item.Element("low").Value.ToString(),
High = item.Element("high").Value.ToString(),
Time = item.Element("nicedate").Value.ToString()
};
And when I foreach through items, only one item gets selected. I'm not very used to Linq so I can't figure out how to properly construct this statement. Any suggestions?
You need to start the Linq-Xml like so
var items =
from item in
doc.Element("data")
.Elements("item")
Descedants() method returns not only children, but also grand-children, grand-grand-children etc. So, the second tag that gets processed by LINQ is your first <item>'s <date> and it isn't processed (I think there should be an exception here, can't check at the moment).
Replace your Descedants() call to Elements("item"), as suggested by #DaveShaw

Categories

Resources