Sorry for being a total noob here, but maybe s/o can point me in the right direction!?
I got code off the net and am trying to make it work, but it will not :(
the xml is this:
<bla>
<blub>
<this that="one">test one</this>
<this that="two">test two</this>
<this that="three">test three</this>
</blub>
</bla>
And I want to have "this" displayed, where "that" == "two", as you can probably tell ;)
but it will only work for the first (one"), not for "two" or "three".
Why does it not continue to the 2nd and 3rd element? Thanks for any advice!
XElement tata = XElement.Load(#"\\somewhere\test.xml");
var tutu = from titi in tata.Elements("blub")
where (string)titi.Element("this").Attribute("that") == "two"
select titi.Element("this");
foreach (XElement soso in tutu)
{
Console.WriteLine(soso.Value);
}
Your problem is here:
where (string)titi.Element("this").Attribute("that") == "two"
In particular the Element(), which gets the first (in document order) child element with the specified XName which happens to be test one in your case.
Instead, use XDocument rather than XElement and look at all elements in the document:
XDocument tata = XDocument.Load(#"\\somewhere\test.xml");
var tutu = from titi in tata.Descendants("this")
where titi.Attribute("that").Value == "two"
select titi;
Related
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
I've been searching all the weekend, but haven't found any results, I'm looking for a program or C# code, that i select a file, and i can select a node, and the child node i want the mother node to be ordered by,
<SelectedProfile>
<Name>Default</Name>
<InstalledAssemblies>
<LeagueSharpAssembly>
<InstallChecked>true</InstallChecked>
<InjectChecked>true</InjectChecked>
<Status>Ready</Status>
<DisplayName>Library - Common</DisplayName>
</LeagueSharpAssembly>
</InstalledAssemblies>
</SelectedProfile>
I want to select "LeagueSharpAssembly", and order all the "LeagueSharpAssembly" this by "DisplayName"
Any of u know something?
Using Linq to XML
var result = x.Descendants("LeagueSharpAssembly")
.OrderBy(r => r.Descendants("DisplayName").Single().Value);
This answer works, but I first misread the question, so the query should be:
var result=x.Descendants("LeagueSharpAssembly")
.OrderBy(r=>r.Element("DisplayName").Value);
This was posted first by #RichardSchneider, so if you are going to use Linq to XML his answer should be accepted.
You can use Linq and XML to select and order the elements, then a simple foearch to process them.
var doc = XDocument.Load("...");
var assemblies = doc.Descendants("LeagueSharpAssembly")
.OrderBy(r => r.Element("DisplayName").Value);
foreach (var a in assemblies)
{
Console.WriteLine("Assembly {0}", a.DisplayName);
}
I have problem i'm trying to update a specific part of the XML with the linq query but it doesn't work. So i an xml file:
<?xml version="1.0" encoding="utf-8"?>
<DesignConfiguration>
<Design name="CSF_Packages">
<SourceFolder>C:\CSF_Packages</SourceFolder>
<DestinationFolder>C:\Documents and Settings\xxx</DestinationFolder>
<CopyLookups>True</CopyLookups>
<CopyImages>False</CopyImages>
<ImageSourceFolder>None</ImageSourceFolder>
<ImageDesinationFolder>None</ImageDesinationFolder>
</Design>
</DesignConfiguration>
I want to select the part where the part where there is Design name="somethning" and get the descendants and then update the descendants value that means this part:
<SourceFolder>C:\CSF_Packages</SourceFolder>
<DestinationFolder>C:\Documents and Settings\xxx</DestinationFolder>
<CopyLookups>True</CopyLookups>
<CopyImages>False</CopyImages>
<ImageSourceFolder>None</ImageSourceFolder>
<ImageDesinationFolder>None</ImageDesinationFolder>
I have this code:
XDocument configXml = XDocument.Load(configXMLFileName);
var updateData = configXml.Descendants("DesignConfiguration").Elements().Where(el => el.Name == "Design" &&
el.Attribute("name").Value.Equals("CSF_Packages")).FirstOrDefault();
configXml.Save(configXMLFileName);
I'm getting the null data in the updateData varibale. When I'm trying the Descendat's function through QuickWatch it also returns a null value. When I'm checking the configXML variable it has data that is my whole xml. What am I doing wrong?
Try this:
var updateData =
confixXml
.Root //Root Element
.Elements("Design") //All elements under root called Design
.Where(element => (String)element.Attribute("name") == "AFP_GRAFIKA") //Find the one with the name Attribute of AFP_GRAFIKA
.FirstOrDefault(); //Grab the first one it finds or return null.
if (updateData != null)
{
var myElements =
updateData
.Elements(); //All the elements under the Design node
}
XDocument xml = XDocument.Load("");
XElement settings = (from children in xml.Descendants("DesignConfiguration")
where children.Name.Equals("Design") && children.Attribute("name").Equals("CSF_Packages")
select children).FirstOrDefault();
settings.Element("SourceFolder").SetValue("filepath");
settings.Element("CopyImages").SetValue(true);
Ok, so I've managed to fix the problem. I don't know why but it worked. It seems that the Descendants function returns null as a stand alone function but with linq it works. So for my solution only thing what should be done is this:
var updateData = (from s in configXml.Descendants("Design")
where s.Attribute("name").Value == design.DesignName
select s).First();
At first before I sent you my question I've tried this but I didn't have the select s part. Besides when I wrote the where s.Atribute part in the curly brackets I've inserted the design.DesignName object instead of the name of the attribute. So no it works ok. Thanks for your help and everything. Til nex time. Have a nice day/night everyone :)
Because DesignConfiguration was your root node, the Descendants("DesignConfiguration) was returning null. By using the .Descendants("Design"), you were looking at child nodes, not the self.
I have just written some code, which as i was writing i thought, this is going to be a nice generic method for searching for a particular node. When i finished i actually realised it was a mess :D
public String sqlReading(String fileName, String path, String nodeId )
{
XmlDocument doc = new XmlDocument();
doc.Load(fileName);
XmlNodeList names = doc.SelectNodes(path);
foreach (XmlNode xmlDocSearchTerm in names)
{
//if the attribute of the node i start at is the same as where i am now
if (xmlDocSearchTerm.Attributes.Item(0).Value.ToString().Equals(nodeId))
{
//get a list of all of its child nodes
XmlNodeList childNodes = xmlDocSearchTerm.ChildNodes;
foreach (XmlNode node in childNodes)
{
//if there is a node in here called gui display, go inside
if (node.Name.Equals("GUIDisplay"))
{
XmlNodeList list = node.ChildNodes;
//find the sqlsearchstring tag inside of here
foreach (XmlNode finalNode in list)
{
if (finalNode.Name.Equals("sqlSearchString"))
{
return node.InnerText;
}
}
}
}
}
}
return "";
}
What i intended to do was based on a path - i would start and check to see if the element had the id i was looking for, if it did then i wanted to get inside there and not stop going until i got to the sqlsearchstring tag which was buried two levels deeper. I have managed that, but the issue here is that now i seem to have almost hardcoded a path to the tag opposed to looping there. How could i change my code to stop me from doing this?
Its from the second foreach where its going wrong imo.
Thanks
Haven't tested it but I believe something like this would work, by using a xpath. However I'm not sure the name of the attribute, or is it always the first attribute?
public String sqlReading(String fileName, String path, String nodeId)
{
XmlDocument doc = new XmlDocument();
doc.Load(fileName);
XmlNode foundNode = doc.SelectNodes(path).SelectSingleNode("*[#id='" + nodeId + "']/GUIDisplay/sqlSearchString");
if (foundNode != null)
return foundNode.InnerText;
return string.Empty;
}
Im not sure if this is exaclty right (as I dont have an XML document to try it with, but something similar should work
var innerTexts = XDocument.Load(fileName)
.Elements(path)
.Where(n => n.Attributes().ElementAt(0).Value == nodeId)
.SelectMany(n => n.Elements())
.Where(n => n.Name == "GUIDisplay")
.SelectMany(n => n.Elements())
.Where(n => n.Name == "sqlSearchString")
.Select(n => n.ToString());
I would say recursion is a safe bet (for iterating through nested child nodes) Though, from what I gather, the structure remains the same. And with that in mind, why not use [XmlDocumentObj].SelectSingleNode("/[nodeId='"+nodeId+"']") (or some facsimile) instead? This has the ability to search by attribute name, unless the XML structure is always changed and you never have constant tag (in which case XPath is probably a good idea).
I have an XML file formatted like this:
<?xml version="1.0" encoding="utf-8"?>
<Snippets>
<Snippet name="abc">
<SnippetCode>
testcode1
</SnippetCode>
</Snippet>
<Snippet name="xyz">
<SnippetCode>
testcode2
</SnippetCode>
</Snippet>
...
</Snippets>
I have populated a listbox with the snippet name, and it works fine so far. For example (I haven't added any real snippets yet btw), my listbox contains:
abc
xyz
123
When I click on an item in the listbox, I want the snippet code of that item to be inserted into a textbox. Like if abc was clicked, testcode1 should be inserted into the textbox. I used this code on the double click event:
XDocument doc = XDocument.Load(Application.StartupPath + "\\Snippets.xml");
foreach (XElement xe in doc.Elements("Snippets").Elements("Snippet"))
{
if (listBox1.SelectedItem == xe.Attribute("name"))
{
textbox1.Text = xe.Element("SnippetCode").Value;
}
}
However, nothing gets inserted because it never finds the snippet code value. I added a MessageBox.Show("test"); inside the if statement to check if it executes but it never does. The selected listbox item name and snippetname have the same text, so it's quite strange it isn't ever executing.
Does anyone know what's wrong with my code? Also, does anyone know of a better idea to insert text in the document from the snippet element? This method isn't probably good as performance might be a problem for large XML files.
You're comparing the attribute itself with the value, instead of the attribute's value.
Additionally, I can't remember offhand what the type of ListBox.SelectedItem is, but if it's object then that will be doing a reference comparison instead of equality.
string selected = (string) listBox1.SelectedItem;
XDocument doc = XDocument.Load(Application.StartupPath + "\\Snippets.xml");
foreach (XElement xe in doc.Elements("Snippets").Elements("Snippet"))
{
if (xe.Attribute("name").Value == selected)
{
textbox1.Text = xe.Element("SnippetCode").Value;
}
}
Note that this will fail with an exception if there are any snippets without a "name" attribute. That's probably a good thing if every snippet is meant to have a name attribute - but if they're allowed not to, then using the explicit string conversion instead of the Value property is simple:
string selected = (string) listBox1.SelectedItem;
XDocument doc = XDocument.Load(Application.StartupPath + "\\Snippets.xml");
foreach (XElement xe in doc.Elements("Snippets").Elements("Snippet"))
{
if ((string) xe.Attribute("name") == selected)
{
textbox1.Text = xe.Element("SnippetCode").Value;
}
}
Note that you can also do this through LINQ:
string selected = (string) listBox1.SelectedItem;
XDocument doc = XDocument.Load(Application.StartupPath + "\\Snippets.xml");
string code = doc.Elements("Snippets")
.Elements("Snippet")
.Where(x => x.Attribute("name").Value == selected)
.Select(x => x.Element("SnippetCode").Value)
.FirstOrDefault();
if (code != null)
{
textbox1.Text = code;
}
I figured out the problem, xe.Attribute("name") was returning name="abc" instead of just abc. My bad for not realzing this just after making the above post.