This code works but I want to understand why I don't have the pass the modified XmlDocument back to the caller - a code review I guess ;). I assume that these operations within XmlNode are using "by Ref" internally to operate on the document. I can see that working but I just want to make sure I understand this correctly. I have not found any documentation that addresses this specifically - probably because it is so obvious. So help me out here fellow devs. This is all happening by Ref and I don't have to worry about passing the modified XmlDocument back to the caller correct? Also, is this thread safe? I am using this code as part of a service app.
public void AddNewElement(XmlDocument doc, string elementName)
{
XmlNode selectedNode = doc.SelectSingleNode("//" + elementName);
if (selectedNode == null)
{
var childNodes = doc.LastChild.LastChild.ChildNodes;
XmlNode appendNode = childNodes.Item(childNodes.Count - 1);
XmlNode newNode = appendNode.AppendChild(doc.CreateElement(elementName));
newNode.InnerText = str64;
}
}
Related
I'm having trouble with datascraping on this web address: http://patorjk.com/software/taag/#p=display&f=Graffiti&t=Type%20Something%20.
The problem is: I've written a code that is supposed to grab the contents of a certain node and display it on console. However, the contents withing the node and the specific node itself seem to be unreachable, but I know they exists for the fact that I've created a condition within my code in order to let me know if nodes withing a certain body are being found and it is indeed being found but not displayed for some reason:
private static void getTextArt(string font, string word)
{
HtmlWeb web = new HtmlWeb();
//cureHtml method is just meant to return the http address
HtmlDocument htmlDoc = web.Load(cureHtml(font, word));
if(web.Load(cureHtml(font, word)) != null)
Console.WriteLine("Connection Established");
else
Console.WriteLine("Connection Failed!");
var nodes = htmlDoc.DocumentNode.SelectSingleNode(nodeXpath).ChildNodes;
foreach(HtmlNode node in nodes)
{
if(node != null)
Console.WriteLine("Node Found.");
else
Console.WriteLine("Node not found!");
Console.WriteLine(node.OuterHtml);
}
}
private const string nodeXpath = "//div[#id='maincontent']";
}
The Html displayed by the website looks like this:
The Html code within the website. Arrows point at the node I'm trying to reach and the content within it I'm trying to display on the console
When I run my code on console to check for the node and its contents and try to display the OuterHtml string of the Xpath, this is how console will display it to me:
Console Window Display
I hope some of you are able to explain to me why is it behaving this way. I've tried all kinds of searches on google for two days trying to figure out the problem for no use. Thank you all in advance.
The content you desire is loaded dynamically.
Use the HtmlWeb.LoadFromBrowser() method instead. Also, check htmlDoc for null, instead of calling it twice. Your current logic doesn't guarantee your state.
HtmlDocument htmlDoc = web.LoadFromBrowser(cureHtml(font, word));
if (htmlDoc != null)
Console.WriteLine("Connection Established");
else
Console.WriteLine("Connection Failed!");
Also, you'll need to decode the result.
Console.WriteLine(WebUtility.HtmlDecode(node.OuterHtml));
If this doesn't work, then your cureHtml() method is broken, or you're targeting .NET Core :)
I have an XML file that collects information with Button_Click, so it starts off empty.
XML Sample
<marina>
<dockone>
</dockone>
<docktwo>
</docktwo>
</marina>
When I submit information from a textbox, a new XmlNode is created called slipone, and another XmlNode called reg is nested within that.
XML Sample 2
<marina>
<dockone>
<slipone>
<reg>12345</reg>
<slipone>
</dockone>
<docktwo>
</docktwo>
</marina>
I have attempted to create an if/else statement that will add a new XmlNode called sliptwo, with reg still nested within it, if slipone already has text, like so:
<marina>
<dockone>
<slipone>
<reg>12345</reg>
<slipone>
<sliptwo>
<reg>67890</reg>
<sliptwo>
</dockone>
<docktwo>
</docktwo>
</marina>
However the closest I have gotten is another XMlnode is still created, however it labels itself as slipone, and I am not sure what I am doing wrong:
<marina>
<dockone>
<slipone>
<reg>12345</reg>
<slipone>
<slipone>
<reg>67890</reg>
<slipone>
</dockone>
<docktwo>
</docktwo>
</marina>
This is an example of what I have been playing around with. Ignore the operators as I have resorted to trial and error but still have gotten nowhere. Please help!
C# Example
XmlDocument XmlDocObj1 = new XmlDocument();
XmlDocObj1.Load(Server.MapPath("~/App_Data/SlipData.xml"));
XmlNode rootnode1 = XmlDocObj1.SelectSingleNode("marina/dockone");
XmlNode dockone = rootnode1.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "slipone", ""));
XmlNode docktwo = rootnode1.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "sliptwo", ""));
XmlNode dockthree = rootnode1.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "slipthree", ""));
if (regfinal.Text != dockone.InnerText)
{
dockone.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "Reg", "")).InnerText = regfinal.Text;
XmlDocObj1.Save(Server.MapPath("/App_Data/SlipData.xml"));
}
else if (regfinal.Text == dockone.InnerText)
{
docktwo.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "Reg", "")).InnerText = regfinal.Text;
XmlDocObj1.Save(Server.MapPath("/App_Data/SlipData.xml"));
}
Your logic isn't going to do what I think you are saying since the only time (regfinal.Text != dockone.InnerText) will evaluate to false is when you enter nothing in your text control.
I believe you might mean to say if dockone exists then create another node called docktwo. This will require you to change your logic.
Some very simple code to get you a bit farther down the path. Not intended to be perfect or solve all problems...
private void button1_Click(object sender, EventArgs e)
{
XmlDocument XmlDocObj1 = new XmlDocument();
XmlDocObj1.Load(AppDomain.CurrentDomain.BaseDirectory.ToString()+"test.xml");
XmlNode rootnode1 = XmlDocObj1.SelectSingleNode("marina/dockone");
XmlNode dockone = rootnode1.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "slipone", ""));
XmlNode docktwo = rootnode1.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "sliptwo", ""));
XmlNode dockthree = rootnode1.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "slipthree", ""));
//jsh: old logic
//if (textBox1.Text != dockone.InnerText)
//new logic to test whether we have already created the dockone node which should only occur once
//you already have the logic for selecting the dockone node above...now just test if you already have it.
//NOTE: you may actually want a switch statement given that you avhe dockone, docktwo, and dockthree or at least another
// if statement to see if docktwo has been created and thus creaste dockthree.
if (rootnode1 == null )
{
dockone.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "Reg", "")).InnerText = textBox1.Text;
XmlDocObj1.Save(AppDomain.CurrentDomain.BaseDirectory.ToString() + "test.xml");
}
//else if (textBox1.Text == dockone.InnerText) jsh: old logic
else
{
docktwo.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "Reg", "")).InnerText = textBox1.Text;
XmlDocObj1.Save(AppDomain.CurrentDomain.BaseDirectory.ToString() + "test.xml");
}
}
I keep getting errors with this, and can't see what I'm doing wrong.
Here's the code
private void _FixSave_Offline_Load(object sender, EventArgs e)
{
System.Xml.XmlDocument NewGame = new System.Xml.XmlDocument();
NewGame.Load(Application.StartupPath + "//Files//Checks_Offline.xml");
foreach (System.Xml.XmlNode nameNode in NewGame.SelectNodes("//Games//NewGame"))
{
listView1.Items.Add(nameNode.Attributes["Name"].InnerText);
}
}
And here is the XML Layout
<Games>
<NewGame>
<Name></Name>
<Check></Check>
<Static></Static>
<Location></Location>
<Start></Start>
<Length></Length>
<FoundBy></FoundBy>
<Verified></Verified>
</NewGame>
Here's is the error I keep getting
and visual studio highlights the following code:
listView1.Items.Add(nameNode.Attributes["Name"].InnerText);
I've tried using not only "//" but also "/" so anything that will fix this will be more than welcome, b/c I can't for the life of me see what I'm doing wrong.
At a glance, you're looking for an attribute with the name of "Name", but there are no attributes on any of the XML elements in your example.
I believe you want the content of the Name node:
foreach (System.Xml.XmlNode nameNode in NewGame.SelectNodes("//Games//NewGame/Name"))
{
listView1.Items.Add(nameNode.Value);
}
You might have to play with the XPath expression a bit, depending on the actual structure of your XML document.
I couldn't see your XML example for some reason, but make sure you are distinguishing between Elements and Attributes
Also, make sure that the attribute/element is spelled "Name" exactly. I believe it is case sensitive.
--
Edit: Now I am able to view your XML, it appears that "Name" is actually an element, rather than an attribute.
Try using the Item property, or the Value property instead of nameNode.Attributes.
I got it to work. Turns out that I the error was due to virtuallist = true. Tim I modified your code above just a little to get the result I wanted. Here's the code for anyone to use for future ref.
private void _FixSave_Offline_Load(object sender, EventArgs e)
{
System.Xml.XmlDocument NewGame = new System.Xml.XmlDocument();
NewGame.Load(Application.StartupPath + "//Files//Checks_Offline.xml");
foreach (System.Xml.XmlNode nameNode in NewGame.SelectNodes("//Games//NewGame/Name"))
{
listView1.Items.Add(nameNode.InnerText);
}
}
And here's a quick screenshot for the given result.
Hope this helps others as well. Thanks to the above people who commented me on this, and big thanks to Tim.
Im trying to use the ReplaceChild function. the code works, and no exceptions are thrown, but when I print XML to the screen it seems as if the function didnt work. the original node is the but not the new one.
private void convertClubComp(XmlDocument doc)
{
XmlNode sessionNode = doc.SelectSingleNode("Session");
XmlNode clubsNode = doc.CreateNode(XmlNodeType.Element, "Clubs", "");
XmlNode playerNode = sessionNode.SelectSingleNode("Players").SelectSingleNode("Player");
XmlNode groupNode = sessionNode.SelectSingleNode("Players").SelectSingleNode("Player").SelectSingleNode("Groups");
Console.WriteLine(playerNode.Name);
clubsNode = doc.ImportNode(groupNode, true);
playerNode.ReplaceChild(clubsNode, sessionNode.SelectSingleNode("Players").SelectSingleNode("Player").SelectSingleNode("Groups"));
Console.WriteLine(clubsNode.FirstChild.FirstChild.Name);
Console.WriteLine("!"+playerNode.FirstChild.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.Name);
}
Thanks
Ok, what's happening is when you do the ImportNode() it is replacing your reference to the new node you created with a clone of the other node. I think what you're looking for is something along the lines of this:
private void convertClubComp(XmlDocument doc)
{
XmlNode sessionNode = doc.SelectSingleNode("Session");
XmlNode playerNode = sessionNode.SelectSingleNode("Players").SelectSingleNode("Player");
XmlNode groupNode = playerNode.SelectSingleNode("Groups");
Console.WriteLine(playerNode.Name);
XmlNode clubsNode = doc.CreateElement("Clubs", "");
foreach (XmlNode child in groupNode.ChildNodes)
{
clubsNode.AppendChild(child.CloneNode(true));
}
foreach (XmlAttribute attribute in groupNode.Attributes)
{
clubsNode.Attributes.Append((attribute.Clone() as XmlAttribute));
}
playerNode.ReplaceChild(clubsNode, groupNode);
Console.WriteLine(clubsNode.FirstChild.FirstChild.Name);
Console.WriteLine("!" + playerNode.FirstChild.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.Name);
}
On a separate note, don't use SelectSingleNode() or SelectNodes() if you already have a reference. It's really poor practice and in larger systems can kill your performance.
Here's my XML file:
<?xml version="1.0" encoding="utf-8" ?>
<Hero>
<Legion>
<Andromeda>
<HeroType>Agility</HeroType>
<Damage>39-53</Damage>
<Armor>3.1</Armor>
<MoveSpeed>295</MoveSpeed>
<AttackType>Ranged(400)</AttackType>
<AttackRate>.75</AttackRate>
<Strength>16</Strength>
<Agility>27</Agility>
<Intelligence>15</Intelligence>
<Icon>Images/Hero/Andromeda.gif</Icon>
</Andromeda>
<WitchSlayer>
<HeroType>Agility</HeroType>
<Damage>39-53</Damage>
<Armor>3.1</Armor>
<MoveSpeed>295</MoveSpeed>
<AttackType>Ranged(400)</AttackType>
<AttackRate>.75</AttackRate>
<Strength>16</Strength>
<Agility>27</Agility>
<Intelligence>15</Intelligence>
<Icon>Images/Hero/Andromeda.gif</Icon>
</WitchSlayer>
</Legion>
</Hero>
Here's my method, but it isn't working so I don't know what to do.
public string GetHeroIcon(string Name)
{
//Fix later. Load the XML file from resource and not from the physical location.
HeroInformation = new XPathDocument(#"C:\Users\Sergio\Documents\Visual Studio 2008\Projects\Erth v0.1[WPF]\Tome of Newerth v0.1[WPF]\InformationRepositories\HeroRepository\HeroInformation.xml");
Navigator = HeroInformation.CreateNavigator();
Navigator.MoveToRoot();
Navigator.MoveToChild("Witch","Legion");
string x = "";
do
{
x += Navigator.Value;
} while (Navigator.MoveToNext());
return x;
}
I need help making a method that recieves a string parameter "Name" and then return all of the attributes of the XML element.
In pseudo-code:
public void FindHero(string HeroName)
{
//Find the "HeroName" element in the XML file.
//For each tag inside of the HeroName parent element,
//add it to a single string and blast it out through a MessageBox.
}
I'm LEARNING how to use this, please don't leave snobby remarks like, "we won't do this for you." I'm not asking for something groundbreaking here, just a simple use case for what I need on my program and for my learning nothing else. :D I'm doing the whole app in WPF and I can literally say that I've not done ONE single thing with previous knowledge, I'm doing this just to learn new things in my spare time.
Thanks a bunch SO, you rock!
private static string GetHeroIcon(string name)
{
XDocument doc = XDocument.Load("C:/test.xml");
return doc.Descendants(name).Single().Element("Icon").Value;
}
First off, since you've tagged this question WPF, you should know that WPF has excellent support for binding directly to XML data. You can then for instance map an image in the GUI directly to the Icon element in the XML file. See this link for example: http://www.longhorncorner.com/UploadFile/cook451/DataBindingXAML12102005134820PM/DataBindingXAML.aspx (first hit on google for "wpf databinding xml")
From code, you can create an XPathDocument from your XML file, then get a Navigator and finally run custom XPath queries on it, like so:
// Get's the value of the <icon> tag for a hero
var node = myNavigator.SelectSingleNode("/Legion/Hero/" + nameOfHero + "/Icon");
var icon = node.Value;
// To get all the nodes for that hero, you could do
var nodeIter = myNavigator.Select("/Legion/Hero/" + nameOfHero)
var sb = new StringBuilder();
while (nodeIter.MoveNext())
{
sb.AppendLine(nodeIter.Current.Name + " = " + nodeIter.Current.Value);
}
MessageBox.Show(sb.ToString());
See this kb article for an example.
DISCLAIMER: I copied and pasted the code from my code and did some refactoring in this window. It may not compile on first run but that may mean that it takes 10 minutes to get it to where it needs to be.
I would strongly recommend that you use XML deserialization. It's object oriented, type-safe, and just flat out slick.
Try this:
1) Create a series of classes: One for Hero, Legion, Witchslayer, and Andromeda.
Here is an example of the Andromeda class:
using System.Xml.Serialization;
[XmlRoot( "Andromeda" )]
public class Andromeda
{
[XmlElement( "Damage" )]
public String Damage
{
get;set;
}
[XmlElement( "Armor" )]
public double Armor
{
get;set;
}
}
The Hero class should contain an instance of Legion and Legion should contain the rest to mimic the layout of the XML packet.
2) Use the XmlSerializer to deserialize the data:
XmlSerializer xmlSerializer = new XmlSerializer( typeof( Hero ) );
using ( StringReader reader = new StringReader( xmlDataString ) )
{
Hero hero = ( Hero ) xmlSerializer.Deserialize( reader );
}
If you set it up right, you'll be left with a hero instance that contains the nested objects and all of the data. Cool, huh?