LINQ Except query with an XElement - c#

I have a data set that I receive from a service. The data comes in XML format. We are given an XElement object with all the data. The structure of the XML document is very simple. Looks like this:
<root>
<dataPoint>
<id>1</id>
<param1>somedata</param1>
<param2>somedata</param2>
</dataPoint>
<dataPoint>
<id>2</id>
<param1>somedata</param1>
<param2>somedata</param2>
</dataPoint>
</root>
Of course, I have a large number of dataPoints. I also have a list (List) with the id's of dataPoints being displayed in a GUI. What I'd like to have is the dataPoints that ARE NOT displayed on the GUI so I can manipulate only those and not the whole data set.
Thanks

var toDisplay = new List<string>() { "2" };
var xDoc = XElement.Load(.....);
var dPoints = xDoc.Descendants("dataPoint")
.Where(d => !toDisplay.Contains(d.Element("id").Value));
var newXml = new XElement("root",dPoints).ToString();

Here's one way using a mutable dictionary and giving you a dictionary of points and corresponding nodes:
var xml = "<root> \r\n <dataPoint>\r\n <id>1</id>\r\n <param1>somedata</param1>\r\n <param2>so" +
"medata</param2>\r\n </dataPoint>\r\n <dataPoint>\r\n <id>2</id>\r\n <param1>somedata" +
"</param1>\r\n <param2>somedata</param2>\r\n </dataPoint>\r\n</root>";
var doc = XElement.Parse(xml);
var pointsInGui = new List<int> {1, 3, 5};
var dict = doc.Descendants("dataPoint").ToDictionary(e => Convert.ToInt32(e.Element("id").Value));
foreach (var point in pointsInGui) dict.Remove(point);
At this point dict contains 2 => <relevant element>.

Related

Need to select Data from XML file C#

What I'm trying to do is get data from my XML file which has been merged with two others and selected each venue from that file and try to add the value to a list so I can manipulate it further.
This is one of my XML files
<?xml version="1.0" encoding="utf-8" ?>
<Funrun>
<Venue name="Roker Park">
<Runner charity="Cancer Research">
<Firstname>Roger</Firstname>
<Lastname>Malibu</Lastname>
<Sponsorship>550</Sponsorship>
</Runner>
<Runner charity="Arthritis UK">
<Firstname>Adam</Firstname>
<Lastname>White</Lastname>
<Sponsorship>340</Sponsorship>
</Runner>
</Venue>
</Funrun >
I need to be able to select the venue name and save it to a list. This is what I've got so far:
List<string> VenueNames = new List<string>();
var doc = XDocument.Load("XMLFile1.xml");
var doc2 = XDocument.Load("XMLFile2.xml");
var doc3 = XDocument.Load("XMLFile3.xml");
var combinedUnique = doc.Descendants("Venue")
.Union(doc2.Descendants("Venue"))
.Union(doc3.Descendants("Venue"));
foreach (var venuename in combinedUnique.Elements("Venue"))
{
VenueNames.Add(venuename.Attribute("name").Value));
}
The easiest way I would do it is by including Name and Charity within the XElements they belong to.
What I would recommend you do is first reformat your document so that it looks like this:
<Funrun>
<Venue>
<Name>Roker Park</Name>
<Runner1>
<charity>Cancer Research</charity>
<Firstname>Roger</Firstname>
<Lastname>Malibu</Lastname>
<Sponsorship>550</Sponsorship>
</Runner1>
<Runner2>
<charity>Arthritis UK</charity>
<Firstname>Adam</Firstname>
<Lastname>White</Lastname>
<Sponsorship>340</Sponsorship>
</Runner2>
</Venue>
</Funrun >
Note that you could get even simpler by combining all the elements under "Funrun" (example: "Venue") and just iterate through all of them without having to switch documents.
Next moving over to C#:
List<string> VenueNames = new List<string>();
var doc = XDocument.Load("XMLFile1.xml");
var doc2 = XDocument.Load("XMLFile2.xml");
var doc3 = XDocument.Load("XMLFile3.xml");
foreach (XElement element in doc.Root.Descendants("Venue"))
{
VenueNames.Add(element.Element("Name").Value.ToString());
}
//Copy paste this code for each document you would like to search through, though of course change "doc" to say, "doc2".
So just real quick, what this code will do is it will first open the Root element in the XDocument. It will find Decendants of that element with the name, "Name", and for each of those it will copy its value as a string into your list.
List<string> xmlFilePaths = new List<string>
{
#"Path\\SomeJson.txt",
#"Path\\SomeJson1.txt"
};
var venues = xmlFilePaths.Select(fp => XDocument.Load(fp).Descendants("Venue")?.FirstOrDefault()?.Attribute("name")?.Value).Distinct().ToList();

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

Reading child nodes of xml file [duplicate]

This question already has answers here:
Reading value of an XML node
(3 answers)
Closed 8 years ago.
I have created an Xml file with example contents as follows:
<?xml version="1.0" encoding="utf-8"?>
<W-TIBCPTRs>
<W-TIBCPTR>
<TYPTRT>FDR2 R</TYPTRT>
<CLAFCNO VALIDE="NON">5b1</CLAFCNO>
<NUMCLI>0067781</NUMCLI>
<TYPACT>D</TYPACT>
</W-TIBCPTR>
<W-TIBCPTR>
<TYPTRT>FDR2 R</TYPTRT>
<CLAFCNO>511</CLAFCNO>
<NUMCLI>0068078</NUMCLI>
<TYPACT>D</TYPACT>
</W-TIBCPTR>
</W-TIBCPTRs>
i try this
XmlNodeList rowElements = doc.SelectNodes("W-TIBCPTRs/W-TIBCPTR");
foreach (XmlElement rowElement in rowElements)
{
foreach (XmlElement valueElement in rowElement.ChildNodes)
{
strin[] k=valueElement.Name;
}
}
I need to selectNodes automaticly because I can have a xml file with different nodes
Stock childNodes in array, I need these values (TYPTRT,CLAFCNO,NUMCLI,TYPACT)
It's easy to do with Linq to Xml (I suggest to use it instead old XmlDocument API):
var xdoc = XDocument.Load(path_to_xml);
var result = from t in xdoc.Root.Elements("W-TIBCPTR")
select new {
TYPTRT = (string)t.Element("TYPTRT"),
CLAFCNO = (string)t.Element("CLAFCNO"),
NUMCLI = (string)t.Element("NUMCLI"),
TYPACT = (string)t.Element("TYPACT")
};
Result:
[
{
TYPTRT: "FDR2 R",
CLAFCNO: "5b1",
NUMCLI: "0067781",
TYPACT: "D"
},
{
TYPTRT: "FDR2 R",
CLAFCNO: "511",
NUMCLI: "0068078",
TYPACT: "D"
}
]
NOTE: Currently you are selecting element's name. If you want to get inner text of all sub-elements in array, you can use:
string[] items = rowElement.ChildNodes.OfType<XmlElement>()
.Select(e => e.InnerText)
.ToArray();
That will return array of four strings for each W-TIBCPTR element in your sample xml. Same with Linq to Xml will look like:
var result = from t in xdoc.Root.Elements("W-TIBCPTR")
select t.Elements().Select(e => e.Value).ToArray();
Use Linq to XML
var doc= XDocument.Load(<path>);
var values = from c in doc.Descendants("W-TIBCPTR")
select new
{
TYPTRT = c.Element("TYPTRT").Value,
CLAFCNO = c.Element("CLAFCNO").Value
NUMCLI = c.Element("NUMCLI").Value
TYPACT = c.Element("TYPACT").Value
});

parsing a xml file which contains more than one keyvalues using c#

I have file format
<?xml version='1.0' encoding='us-ascii'?>
<root>
<file id="001">
<filename>ABC.wav</filename>
<value>0.18</value>
</file>
<file id="002">
<filename>EFG.wav</filename>
<value>0.05</value>
<value>0.14</value>
</file>
</root>
I want to parse that USING C#
doc.Load(confidencethresholdFilePath+"\\model.xml");
XmlNodeList nodes = doc.DocumentElement.SelectNodes("/root/file");
List<Result> results = new List<Result>();
foreach (XmlNode node in nodes)
{
Result result = new Result();
result.ASfilename= node.SelectSingleNode("filename").InnerText;
result.resultedSeconds = node.SelectSingleNode("value").InnerText;
results.Add(result);
}
It gives result but misses the second record's second value.How do i get all results without fail.
How about using LINQ to XML?
var xDoc = XDocument.Load("Input.xml");
var results =
xDoc.Root
.Elements("file")
.Select(f => new
{
FileName = (string)f.Element("filename"),
Values = f.Elements("value").Select(v => (string)v).ToList()
})
.ToList();
results will be a list of anonymous type instances with two properties: FileName:string and Values:List<string>. You can easily change it to return List<Record> instead, just change f => new to f => new Record and update properties info.
As you can see, it's much easier to get XML content using LINQ to XML than using old-style XmlSomething classes.
If you want a separate Result for each value and using your non-Linq style, you could change the initial selection. The file name selection will then need to change appropriately.
doc.Load(confidencethresholdFilePath+"\\model.xml");
XmlNodeList nodes = doc.DocumentElement.SelectNodes("/root/file/value");
List<Result> results = new List<Result>();
foreach (XmlNode node in nodes)
{
Result result = new Result();
result.ASfilename= node.SelectSingleNode("../filename").InnerText;
result.resultedSeconds = node.SelectSingleNode("value").InnerText;
results.Add(result);
}

C# - Linq to XML - Exclude elements from query

I have this XML file:
<MyXml>
<MandatoryElement1>value</MandatoryElement1>
<MandatoryElement2>value</MandatoryElement2>
<MandatoryElement3>value</MandatoryElement3>
<CustomElement1>value</CustomElement1>
<CustomElement2>value</CustomElement2>
<MyXml>
All 3 elements that are called 'MandatoryElementX' will always appear in the file. The elements called 'CustomElementX' are unknown. These can be added or removed freely by a user and have any name.
What I need is to fetch all the elements that are not MandatoryElements. So for the file above I would want this result:
<CustomElement1>value</CustomElement1>
<CustomElement2>value</CustomElement2>
I don't know what the names of the custom elements may be, only the names of the 3 MandatoryElements, so the query needs to somehow exclude these 3.
Edit:
Even though this was answered, I want to clarify the question. Here is an actual file:
<Partner>
<!--Mandatory elements-->
<Name>ALU FAT</Name>
<InterfaceName>Account Lookup</InterfaceName>
<RequestFolder>C:\Documents and Settings\user1\Desktop\Requests\ALURequests</RequestFolder>
<ResponseFolder>C:\Documents and Settings\user1\Desktop\Responses</ResponseFolder>
<ArchiveMessages>Yes</ArchiveMessages>
<ArchiveFolder>C:\Documents and Settings\user1\Desktop\Archive</ArchiveFolder>
<Priority>1</Priority>
<!--Custom elements - these can be anything-->
<Currency>EUR</Currency>
<AccountingSystem>HHGKOL</AccountingSystem>
</Partner>
The result here would be:
<Currency>EUR</Currency>
<AccountingSystem>HHGKOL</AccountingSystem>
You can define a list of mandatory names and use LINQ to XML to filter:
var mandatoryElements = new List<string>() {
"MandatoryElement1",
"MandatoryElement2",
"MandatoryElement3"
};
var result = xDoc.Root.Descendants()
.Where(x => !mandatoryElements.Contains(x.Name.LocalName));
Do you have created this xml or do you get it by another person/application?
If it's yours I would advise you not to number it. You can do something like
<MyXml>
<MandatoryElement id="1">value<\MandatoryElement>
<MandatoryElement id="2">value<\MandatoryElement>
<MandatoryElement id="3">value<\MandatoryElement>
<CustomElement id="1">value<\CustomElement>
<CustomElement id="2">value<\CustomElement>
<MyXml>
In the LINQ-Statement you don't need the List then.
Your question shows improperly formatted XML but I am assuming that is a typo and the real Xml can be loaded into the XDocument class.
Try this...
string xml = #"<MyXml>
<MandatoryElement1>value</MandatoryElement1>
<MandatoryElement2>value</MandatoryElement2>
<MandatoryElement3>value</MandatoryElement3>
<CustomElement1>value</CustomElement1>
<CustomElement2>value</CustomElement2>
</MyXml> ";
System.Xml.Linq.XDocument xDoc = XDocument.Parse(xml);
var result = xDoc.Root.Descendants()
.Where(x => !x.Name.LocalName.StartsWith("MandatoryElement"));
lets say TestXMLFile.xml will contain your xml,
XElement doc2 = XElement.Load(Server.MapPath("TestXMLFile.xml"));
List<XElement> _list = doc2.Elements().ToList();
List<XElement> _list2 = new List<XElement>();
foreach (XElement x in _list)
{
if (!x.Name.LocalName.StartsWith("Mandatory"))
{
_list2.Add(x);
}
}
foreach (XElement y in _list2)
{
_list.Remove(y);
}

Categories