Need to select Data from XML file C# - 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();

Related

How to seperate strings from a serialized XML node

I have an serialized XML file. This shows the relevant part:
I am reading this XML file with this (code snippet):
temp = Path.GetFileNameWithoutExtension(s);
var document = new XmlDocument();
document.Load(s);
var root = document.DocumentElement;
var node = root["ScenarioDescription"];
var text = node?.InnerText;
var ArmyNode = root["ArmyFiles"];
var ArmyText = ArmyNode?.InnerText;
However, ArmyText returns the concatenation of all three strings that make up the ArmyFiles node. I need them as three separate strings. How can I do this?
This code works to read all the strings in the node and place them into a list:
foreach (XmlElement A in ArmyNode)
{
var ArmyTemp = A.InnerText;
ArmyList.Add(ArmyTemp);
}
var ArmyText = ArmyNode?.InnerText;

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

Deleting node from string with xml structure

I have an string parameter with xml content in it. Basically the string have an XML inside.
string S = funcThatReturnsXML (parameters);
S have the next text:
<?xml version="1.0" encoding="utf-8" ?>
<tagA>
<tagB>
<tagBB>
..
.
.
</tagBB>
.
.
</tagB>
<tagC>
..
..
.
</tagC>
</tagA>
The funcThatReturnsXML (parameters) creates an XmlDocument object but the return it as a string, I cant change this function, to much stuff works with it.
Tried to create XmlDocument objetc but the SelectSingleNode return null.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(S);
XmlNode root = xmlDoc.SelectSingleNode("tagB");
How can I delete from string S (not XML Object) specific node, for example <tagB>
EDIT: this is the XML I tested with:
<?xml version="1.0" ?>
- <Request xmlns:xsi="http://www.mysite.com" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
- <info xmlns="http://www.mysite.com">
<RequestTR>54</RequestTR>
<time>2013-12-22</time>
</info>
- <Parameters xmlns="http://www.mysite.com">
<id>3</id>
<name>2</name>
</Parameters>
<title>Request</title>
</Request>
Try this:
string S = funcThatReturnsXML(parameters);
var doc = XDocument.Parse(S);
var nodeToRemove = doc.Descendants("tagB");
nodeToRemove.Remove();
That will remove all nodes named "tagB" from string S which contains xml.
UPDATE 1:
Sorry, i missed to include one more line:
S = doc.ToString();
My first code above removed "tagB" from doc but didnt save it back to S variable.
UPDATE 2:
I tested with following xml which contain attribute:
<tagA attribute="value">
<tagB>
<tagBB>
</tagBB>
</tagB>
<tagC></tagC>
</tagA>
and the output of Console.WriteLine(S):
<tagA attribute="value">
<tagC></tagC>
</tagA>
UPDATE 3:
Given your updated xml format, I know why my previous code didn't work for you. That was because your xml have namespace (xmlns) declared. The solution is to use LocalName when searching for the node to be removed, that will search for node name while ignoring its namespace. The follwoing example shows how to remove all "info" node:
var doc = XDocument.Parse(S);
var nodeToRemove = doc.Descendants().Where(o => o.Name.LocalName == "info");
nodeToRemove.Remove();
S = doc.ToString();
If you can determine the particular outer element to remove from the returned XML, you could use LINQ to XML:
var returnedXml = funcThatReturnsXML(parameters);
var xmlElementToRemove = funcThatReturnsOuterElement(returnedXml);
var xelement = XElement.Load("XmlDoc.txt");
xelement.Elements().Where(e => e.Name == xmlElementToRemove).Remove();
For example:
using System.Linq;
using System.Xml.Linq;
class Program
{
static void Main(string[] args)
{
// pretend this is the funThatReturnsXML return value
var returnedXml = "<tagB><tagBB></tagBB></tagB>";
// get the outer XML element name
var xmlElementToRemove = GetOuterXmlElement(returnedXml);
// load XML from where ever
var xelement = XElement.Load("XmlDoc.txt");
// remove the outer element and all subsequent elements
xelement.Elements().Where(e => e.Name == xmlElementToRemove).Remove();
}
static string GetOuterXmlElement(string xml)
{
var index = xml.IndexOf('>');
return xml.Substring(1, index - 1);
}
}
Note that the above is a "greedy" removal method, if there is more than once element with the name returned via the GetOuterXmlElemet method they will all be removed. If you want a specific instance to be removed then you will require something more sophisticated.
Building on your edit:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(S);
var nodeA = xmlDoc.SelectSingleNode("/tagA");
var nodeB = nodeA.SelectSingleNode("tagB");
nodeA.RemoveChild(nodeB);
To remove (possibly) multiple tagB nodes in unknown positions, you may try:
var bees = xmlDoc.SelectNodes("//tagB");
foreach (XmlNode bee in bees) {
var parent = bee.ParentNode;
parent.RemoveChild(bee);
}

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

Update XAttribute Value where XAttribute Name = X

I have the following code which creates an XML file with a bunch of order information. I'd like to be able to update an entry in this XML file instead of deleting everything and re-adding everything again.
I know I can do this:
xElement.Attribute(attribute).Value = value;
But that will change every attribute with the same name as attribute holds. How can I only change the value of something when the entry's Id equals "jason", for example? Would I need to Load the XML file, iterate over the entire file until it finds a match for the attribute I want to change, then change it, and then save the file again?
Any help/suggestions are greatly appreciated.
XElement xElement;
xElement = new XElement("Orders");
XElement element = new XElement(
"Order",
new XAttribute("Id", CustomId),
new XAttribute("Quantity", Quantity),
new XAttribute("PartNo", PartNo),
new XAttribute("Description", Description),
new XAttribute("Discount", Discount),
new XAttribute("Freight", Freight),
new XAttribute("UnitValue", UnitValue),
new XAttribute("LineTotal", LineTotal)
);
xElement.Add(element);
xElement.Save(PartNo + ".xml");
Here's what my XML file looks like:
<?xml version="1.0" encoding="utf-8"?>
<Orders>
<Order Id="V45Y7B458B" Quantity="2" PartNo="5VNB98" Description="New Custom Item Description" Discount="2.00" Freight="2.90" UnitValue="27.88" LineTotal="25.09" />
<Order Id="jason" Quantity="2" PartNo="jason" Description="New Custom Item Description" Discount="2.00" Freight="2.90" UnitValue="27.88" LineTotal="25.09" />
</Orders>
Something like this:
var doc = XDocument.Load("FileName.xml");
var element = doc.Descendants("Order")
.Where(arg => arg.Attribute("Id").Value == "jason")
.Single();
element.Attribute("Quantity").Value = "3";
doc.Save("FileName.xml");
First you need to search for the element that you want to update. If you find it, do the update. Just remember to save the XDocument back to the file when you're done.
XDocument doc = ...;
var jason = doc
.Descendants("Order")
.Where(order => order.Attribute("Id").Value == "jason") // find "jason"
.SingleOrDefault();
if (jason != null) // if found,
{
// update something
jason.Attribute("Quantity").SetValue(20);
}
doc.Save(...); // save if necessary
Since you created the XML file, you know the root element of the XML so you can use this code to get the particular element you want:
TaxonPath = XElement.Parse(xml as string);
txtSource.Text = FindGetElementValue(TaxonPath, TaxonPathElement.Source);
XElement FindGetElementValue(XElement tree,String elementname)
{
return tree.Descendants(elementName).FirstOrDefault();
}
With this, you can get the element, check its value, and change it as you desire.

Categories