LINQ multiple columns - c#

<root>
<data1>
<Element1>Value</Element1>
<Element2>Value</Element2>
<Element3>Value</Element3>
</data1>
<data2>
<Element1>Value</Element1>
<Element2>Value</Element2>
</data2>
</root>
From the above XML I would like to make an XML looking like this:
<root>
<d1e1>value<d1e1>
<d1e2>value<d1e2>
<d2e1>value<d2e1>
</root>
What is the most efficient way to process that?
Foreach or Linq in theory Linq should be faster in most cases and speed is of the essence for this project
Any idea?

The idea was to just select X nodes out of a pool of Y and the example here is simplified to show you the problem. In general it is like that I have a multi level xml that I needed to flat out to only have one sublevel (aka root + level1) but from the source I only need to have certain elements that are of interest to me.
Anyway the issiue is solved cos I done it with foreach cos I found out that if you have an shema specified in the xml but not accessable LINQ dosent whant to work anyway.
the solution was like this:
I made a function:
public System.Xml.XmlElement GetSubElement(XmlElement Parent, string element)
{
System.Xml.XmlElement ret = null;
if (Parent == null)
return ret;
XmlNodeList ContentNodes = Parent.GetElementsByTagName(element);
if (ContentNodes.Count > 0)
{
XmlNode node = ContentNodes.Item(0);
ret = (XmlElement)node;
}
return ret;
}
I made a foreach loop on the area that was repeating
I got the elements that where out of the repeating context with the above function.
Anyway that solved it for me.
Edit:
Don't know how to get this code to appear properly cos Ctrl+K dosent seem to do it

Related

Creating XML nodes dynamically to fetch its attribute values

I'm working on this using C# .net VS 2013.
I have a scenario where I'm having the structure as below,
<td>
<text text="abc">abc
<tspan text = "bcd">bcd
<tspan text = "def">def
<tspan text = "gef">gef
</tspan>
</tspan>
</tspan>
</text>
</td>
As shown above, I don't know how many tspan nodes will be there, currently I have 3, I may get 4 or more than that.
Once after finding the text node, to get the value of that node I'll use the code,
labelNode.Attributes["text"].Value
to get its adjacent tspan node, I have to use it like
labelNode.FirstChild.Attributes["text"].Value
to get its adjacent tspan node, I have to use it like
labelNode.FirstChild.FirstChild.Attributes["text"].Value
Like this it will keep on going.
Now my question is, if I know that i have 5 tags, is there any way to dynamically add "FirstChild" 5 times to "labelNode" so that I can get the text value of the last node, like this
labelNode.FirstChild.FirstChild.FirstChild.FirstChild.FirstChild.Attributes["text"].Value
If I need 2nd value i need to add it 2 times, if I need 3rd then I need to add it thrice.
Please let me know is there any solution for this.
Please ask me, if you got confused with my question.
Thanking you all in advance.
Rather than adding FirstChild dynamically, I think this would be a simpler solution:
static XmlNode GetFirstChildNested(XmlNode node, int level) {
XmlNode ret = node;
while (level > 0 && ret != null) {
ret = ret.FirstChild;
level--;
}
return ret;
}
Then you could use this function like this:
var firstChild5 = GetFirstChildNested(labelNode, 5);
I would suggesting using Linq to Xml which has cleaner way parsing Xml
Using XElement (or XDocument) you could flatten the hierarchy by calling Descendant method and do all required queries.
ex..
XElement doc= XElement.Load(filepath);
var results =doc.Descendants()
.Select(x=>(string)x.Attribute("text"));
//which returns
abc,
bcd,
def,
gef
If you want to get the last child you could simply use.
ex..
XElement doc= XElement.Load(filepath);
doc.Descendants()
.Last() // get last element in hierarchy.
.Attribute("text").Value
If you want to get third element, you could do something like this.
XElement doc= XElement.Load(filepath);
doc.Descendants()
.Skip(2) // Skip first two.
.First()
.Attribute("text").Value ;
Check this Demo

Is my usage of LINQ to XML correct when I'm trying to find multiple values in the same XML file?

I have to extract values belonging to certain elements in an XML file and this is what I ended up with.
XDocument doc = XDocument.Load("request.xml");
var year = (string)doc.Descendants("year").FirstOrDefault();
var id = (string)doc.Descendants("id").FirstOrDefault();
I'm guessing that for each statement I'm iterating through the entire file looking for the first occurrence of the element called year/id. Is this the correct way to do this? It seems like there has to be a way where one would avoid unnecessary iterations. I know what I'm looking for and I know that the elements are going to be there even if the values may be null.
I'm thinking in the lines of a select statement with both "year" and "id" as conditions.
For clearance, I'm looking for certain elements and their respective values. There'll most likely be multiple occurrences of the same element but FirstOrDefault() is fine for that.
Further clarification:
As requested by the legend Jon Skeet, I'll try to clarify further. The XML document contains fields such as <year>2015</year> and <id>123032</id> and I need the values. I know which elements I'm looking for, and that they're going to be there. In the sample XML below, I would like to get 2015, The Emperor, something and 30.
Sample XML:
<?xml version="1.0" encoding="UTF-8"?>
<documents xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<make>Apple</make>
<year>2015</year>
<customer>
<name>The Emperor</name>
<level2>
<information>something</information>
</level2>
<age>30</age>
</customer>
A code that doesn't parse the whole xml twice would be like:
XDocument doc = XDocument.Load("request.xml");
string year = null;
string id = null;
bool yearFound = false, idFound = false;
foreach (XElement ele in doc.Descendants())
{
if (!yearFound && ele.Name == "year")
{
year = (string)ele;
yearFound = true;
}
else if (!idFound && ele.Name == "id")
{
id = (string)ele;
idFound = true;
}
if (yearFound && idFound)
{
break;
}
}
As you can see you are trading lines of code for speed :-) I do feel the code is still quite readable.
if you really need to optimize up to the last line of code, you could put the names of the elements in two variables (because otherwise there will be many temporary XName creation)
// before the foreach
XName yearName = "year";
XName idName = "id";
and then
if (!yearFound && ele.Name == yearName)
...
if (!idFound && ele.Name == idName)

Reading XML to get value of a tag c#

I have my XML as
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<recordsDirectory>F:/model_RCCMREC/</recordsDirectory>
<transferDirectory>F:/model_RCCMrecTransfered/</transferDirectory>
<logDirectory>F:/model_RCCMLOG/</logDirectory>
<connectionstring>Data Source=192.168.1.7;Initial Catalog=RCCMdb;User ID=genesys;Password=genesys</connectionstring>
<table>RCCMrec</table>
<DBdestination>
<val1>ANI</val1>
<val2>DNIS</val2>
<val3>Date</val3>
<val4>Time</val4>
<val5>ConnId</val5>
<val6>UUID</val6>
<val7>EmployeeId</val7>
<val8>AgentDN</val8>
</DBdestination>
</configuration>
I need the value of the recordsDirectory tag.
I tried this,
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load("C:/Users/yachna/Desktop/RCCM_TOOL/configRCCM.xml");
string bvalue = xmldoc.SelectSingleNode("recordsDirectory").InnerText.ToString();
But got an error saying
Object reference not set to an instance of an object.
Yes, SelectSingleNode("recordsDirectory") will return null, because you're applying that XPath to the document itself - which doesn't have a recordsDirectory element at the top level, it has a configuration element. You want:
xmldoc.SelectSingleNode("configuration/recordsDirectory")
Or go via the root element:
xmldoc.DocumentElement.SelectSingleNode("recordsDirectory")
(Or you can fetch all descendant elements call recordsDirectory, etc. There are plenty of options here.)
Personally I'd suggest changing to use LINQ to XML if you can, as it's a simpler way of using XML, IMO. It's not too bad in the code you've given so far, but as you do more things with XmlDocument you'll run into it being a bit of a pain - relatively speaking, anyway.
You should also consider separating the "fetching the node" from getting the text, so you can validate that you've found the one you want:
XmlNode node = xmldoc.DocumentElement.SelectSingleNode("recordsDirectory");
if (node != null)
{
// Use it
}
else
{
// No such node. What do you want to do?
}
Try this one in your SelectSingleNode
XmlNode node = doc.SelectSingleNode("/configuration/recordsDirectory");
string s = node.InnerText.ToString();
Hi To read the recordsDirectory tag you need to do :
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load("C:/Users/yachna/Desktop/RCCM_TOOL/configRCCM.xml");
string bvalue = xmldoc.SelectSingleNode("configuration/recordsDirectory").InnerText.ToString();
It will work perfactly

Finding element in XDocument?

I have a simple XML
<AllBands>
<Band>
<Beatles ID="1234" started="1962">greatest Band<![CDATA[lalala]]></Beatles>
<Last>1</Last>
<Salary>2</Salary>
</Band>
<Band>
<Doors ID="222" started="1968">regular Band<![CDATA[lalala]]></Doors>
<Last>1</Last>
<Salary>2</Salary>
</Band>
</AllBands>
However ,
when I want to reach the "Doors band" and to change its ID :
using (var stream = new StringReader(result))
{
XDocument xmlFile = XDocument.Load(stream);
var query = from c in xmlFile.Elements("Band")
select c;
...
query has no results
But
If I write xmlFile.Elements().Elements("Band") so it Does find it.
What is the problem ?
Is the full path from the Root needed ?
And if so , Why did it work without specify AllBands ?
Does the XDocument Navigation require me to know the full level structure down to the required element ?
Elements() will only check direct children - which in the first case is the root element, in the second case children of the root element, hence you get a match in the second case. If you just want any matching descendant use Descendants() instead:
var query = from c in xmlFile.Descendants("Band") select c;
Also I would suggest you re-structure your Xml: The band name should be an attribute or element value, not the element name itself - this makes querying (and schema validation for that matter) much harder, i.e. something like this:
<Band>
<BandProperties Name ="Doors" ID="222" started="1968" />
<Description>regular Band<![CDATA[lalala]]></Description>
<Last>1</Last>
<Salary>2</Salary>
</Band>
You can do it this way:
xml.Descendants().SingleOrDefault(p => p.Name.LocalName == "Name of the node to find")
where xml is a XDocument.
Be aware that the property Name returns an object that has a LocalName and a Namespace. That's why you have to use Name.LocalName if you want to compare by name.
You should use Root to refer to the root element:
xmlFile.Root.Elements("Band")
If you want to find elements anywhere in the document use Descendants instead:
xmlFile.Descendants("Band")
The problem is that Elements only takes the direct child elements of whatever you call it on. If you want all descendants, use the Descendants method:
var query = from c in xmlFile.Descendants("Band")
My experience when working with large & complicated XML files is that sometimes neither Elements nor Descendants seem to work in retrieving a specific Element (and I still do not know why).
In such cases, I found that a much safer option is to manually search for the Element, as described by the following MSDN post:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/3d457c3b-292c-49e1-9fd4-9b6a950f9010/how-to-get-tag-name-of-xml-by-using-xdocument?forum=csharpgeneral
In short, you can create a GetElement function:
private XElement GetElement(XDocument doc,string elementName)
{
foreach (XNode node in doc.DescendantNodes())
{
if (node is XElement)
{
XElement element = (XElement)node;
if (element.Name.LocalName.Equals(elementName))
return element;
}
}
return null;
}
Which you can then call like this:
XElement element = GetElement(doc,"Band");
Note that this will return null if no matching element is found.
The Elements() method returns an IEnumerable<XElement> containing all child elements of the current node. For an XDocument, that collection only contains the Root element. Therefore the following is required:
var query = from c in xmlFile.Root.Elements("Band")
select c;
Sebastian's answer was the only answer that worked for me while examining a xaml document. If, like me, you'd like a list of all the elements then the method would look a lot like Sebastian's answer above but just returning a list...
private static List<XElement> GetElements(XDocument doc, string elementName)
{
List<XElement> elements = new List<XElement>();
foreach (XNode node in doc.DescendantNodes())
{
if (node is XElement)
{
XElement element = (XElement)node;
if (element.Name.LocalName.Equals(elementName))
elements.Add(element);
}
}
return elements;
}
Call it thus:
var elements = GetElements(xamlFile, "Band");
or in the case of my xaml doc where I wanted all the TextBlocks, call it thus:
var elements = GetElements(xamlFile, "TextBlock");

C# Linq XML pull out nodes from document

I’m trying to use Linq XML to select a number of nodes and the children but getting terrible confused!
In the example XML below I need to pull out all the <MostWanted> and all the Wanted with their child nodes but without the other nodes in between the Mostwanted and Wanted nodes.
This because each MostWanted can be followed by any number of Wanted and the Wanted relate to the preceding Mostwanted.
I’m even confusing myself typing this up!!!
How can I do this in C#??
<root>
<top>
<NotWanted3>
</NotWanted3>
<MostWanted>
<UniqueKey>1</UniqueKey>
<QuoteNum>1</QuoteNum>
</MostWanted>
<NotWanted2>
<UniqueKey>1</UniqueKey>
<QuoteNum>1</QuoteNum>
</NotWanted2>
<NotWanted1>
<UniqueKey>0001</UniqueKey>
</NotWanted1>
<Wanted>
<Seg>
<SegNum>1</SegNum>
</Seg>
</Wanted>
<Wanted>
<Seg>
<SegNum>2</SegNum>
</Seg>
</Wanted>
<NotWanted>
<V>x</V>
</NotWanted>
<NotWanted3>
</NotWanted3>
<MostWanted>
<UniqueKey>1</UniqueKey>
<QuoteNum>1</QuoteNum>
</MostWanted>
<NotWanted2>
<UniqueKey>1</UniqueKey>
<QuoteNum>1</QuoteNum>
</NotWanted2>
<NotWanted1>
<UniqueKey>0002</UniqueKey>
</NotWanted1>
<Wanted>
<Seg>
<SegNum>3</SegNum>
</Seg>
</Wanted>
<Wanted>
<Seg>
<SegNum>4</SegNum>
</Seg>
</Wanted>
<NotWanted>
<V>x</V>
</NotWanted>
</top>
</root>
Why don't you just use:
XName wanted = "Wanted";
XName mostWanted = "MostWanted";
var nodes = doc.Descendants()
.Where(x => x.Name == wanted || x.Name == mostWanted);
That will retrieve every element called "Wanted" or "MostWanted". From each of those elements you can get to the child elements etc.
If this isn't what you're after, please clarify your question.

Categories