Writing and then removing node from XML doc using C#/Unity3D - c#

I am trying to add nodes to an xml document and then deleting them.
Adding nodes is working, but i cant remove nodes unless i restart the program.
The Write method:
public void writeToExistingDoc (String fileNamePath, int x, int y, int t)
{
string filename = fileNamePath;
string xPos = "" + x;
string yPos = "" + y;
string type = "" + t;
//create new instance of XmlDocument
XmlDocument doc = new XmlDocument ();
//load from file
doc.Load (filename);
//create node and add value
XmlNode node = doc.CreateNode (XmlNodeType.Element, "BUILDING", null);
XmlAttribute atr = doc.CreateAttribute ("x");
XmlAttribute atr2 = doc.CreateAttribute ("y");
XmlAttribute atr3 = doc.CreateAttribute ("type");
atr.Value = xPos;
atr2.Value = yPos;
atr3.Value = type;
node.Attributes.Append (atr);
node.Attributes.Append (atr2);
node.Attributes.Append (atr3);
//add to elements collection
doc.DocumentElement.AppendChild (node);
Debug.Log ("Element added");
//save back
doc.Save (filename);
}
and here is the Remove method:
public void removeBuildingNode (string fileNamePath, int buildingPosX, int buildingPosY)
{
XmlDocument doc = new XmlDocument ();
doc.Load (fileNamePath);
XmlNodeList nodes = doc.SelectNodes ("//BUILDING[#x='" + buildingPosX + "']");
for (int i = nodes.Count - 1; i >= 0; i--) {
Debug.Log("" + i);
nodes[i].ParentNode.RemoveChild (nodes[i]);
}
doc.Save(fileNamePath);
Debug.Log(""+buildingPosX + ", " + buildingPosY);
}
My XML doc looks like this:
<BUILDINGS ID="b">
<BUILDING x="50" y="80" type="1" />
<BUILDING x="25" y="125" type="1" />
<BUILDING x="35" y="125" type="1" />
<BUILDING x="45" y="125" type="1" />
</BUILDINGS>
As i've said, the methods work when i first run the program, use the write method, the restart the program and use the remove method. Wont work on the same running instance.

If you aren't bent on using XmlDocument, this should work...
Using: http://searisen.com/xmllib/extensions.wiki
public void removeBuildingNode (string fileNamePath, int buildingPosX, int buildingPosY)
{
XElement doc = XElement.Load(fileNamePath);
var nodesToRemove = doc.Elements("BUILDING")
.Where(xe => xe.Get("x", int.MinValue) == buildingPosX);
foreach(XElement node in nodesToRemove.ToArray())
node.Remove();
doc.Save(fileNamePath);
Debug.Log(""+buildingPosX + ", " + buildingPosY);
}

Related

Cannot change XmlDocument value?

I have a simple function which will simply change and read the value.
void ParseXml(string XmlFile)
{
string totalval = "";
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(new StringReader(XmlFile));
string xmlPathPattern = "//name";
XmlNodeList mynodelist = xmldoc.SelectNodes(xmlPathPattern);
foreach (XmlNode node in mynodelist)
{
XmlNode name = node.FirstChild;
name.Value = "asd";//here I am trying to change value
totalval = totalval + "Name=" + name.OuterXml + "\n";
}
xmldoc.Save(XmlFile);
print(totalval);
}
This is my .xml file.
<name>John</name>
I can successfully read the value but it is not changing the value from .xml file.After running the program it must be like this
<name>asd</name> .
Where is my mistake ?
Obviously, the XMLFile is not a file path, it is xml string. So, you should define a valid path to save it.
xmldoc.Save("samplefile.xml");
or if you want to set the XmlFile variable with modified xml;
XmlFile = xmldoc.OuterXml;
Complete codes look like;
void ParseXml(string XmlFile)
{
string totalval = "";
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(new StringReader(XmlFile));
string xmlPathPattern = "//name";
XmlNodeList mynodelist = xmldoc.SelectNodes(xmlPathPattern);
foreach (XmlNode node in mynodelist)
{
XmlNode name = node.FirstChild;
name.Value = "asd";//here I am trying to change value
totalval = totalval + "Name=" + name.OuterXml + "\n";
}
//XmlFile = xmldoc.OuterXml;
xmldoc.Save("samplefile.xml");
print(totalval);
}
If I'm not wrong - you need to fire disposable to save stream. Easiest way - wrap with using
void ParseXml(string XmlFile)
{
string totalval = "";
using(XmlDocument xmldoc = new XmlDocument())
{
xmldoc.Load(new StringReader(XmlFile));
string xmlPathPattern = "//name";
XmlNodeList mynodelist = xmldoc.SelectNodes(xmlPathPattern);
foreach (XmlNode node in mynodelist)
{
XmlNode name = node.FirstChild;
name.Value = "asd";//here I am trying to change value
totalval = totalval + "Name=" + name.OuterXml + "\n";
}
xmldoc.Save(XmlFile);
print(totalval);
}
}

Why the Page_Load Event is not able to Get Updated Values from xml file in C# Asp.NET?

I am trying to build a WebPage that includes "Rating of Site". Instead of using Database i am storing two variables to compute Avg of Rating in xml file as
<?xml version="1.0" encoding="utf-8"?>
<data>
<RatingCount>6</RatingCount>
<RatingTotal>17</RatingTotal>
</data>
Here is my Logic Code Both On the Page Load and AJAX Rating Control
Page_Load()
string rootPath = Server.MapPath("~/");
if (File.Exists(rootPath + "data.xml"))
{
XmlDocument xdoc = new XmlDocument();
xdoc.Load(rootPath + "data.xml");
XmlNode ratingCount = xdoc.DocumentElement.SelectSingleNode("/data/RatingCount");
XmlNode ratingTotal = xdoc.DocumentElement.SelectSingleNode("/data/RatingTotal");
string rating = ratingTotal.InnerText;
string count = ratingCount.InnerText;
if (Convert.ToInt32(count) >= 1)
{
double resultRating = Convert.ToSingle(rating) / Convert.ToSingle(count);
Rating1.CurrentRating = Convert.ToInt32(resultRating);
lblerror.Text = resultRating + " Rating in " + count + " Totals Votes";
}
else
{
lblerror.Text = "No Votes Till Now";
}
}
Rating_Control()
string rootPath = Server.MapPath("~/");
if (File.Exists(rootPath + "data.xml"))
{
XmlDocument xdoc = new XmlDocument();
xdoc.Load(rootPath + "data.xml");
XmlNode ratingCount = xdoc.DocumentElement.SelectSingleNode("/data/RatingCount");
XmlNode ratingTotal = xdoc.DocumentElement.SelectSingleNode("/data/RatingTotal");
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter xwrite = XmlWriter.Create(rootPath + #"data.xml", settings);
xwrite.WriteStartDocument();
xwrite.WriteStartElement("data");
string r = (Convert.ToInt32(ratingCount.InnerText) + 1).ToString();
string t = (Convert.ToInt32(ratingTotal.InnerText) + Convert.ToInt32(e.Value)).ToString();
xwrite.WriteElementString("RatingCount", r);
xwrite.WriteElementString("RatingTotal", t);
xwrite.WriteEndElement();
xwrite.WriteEndDocument();
xwrite.Flush();
xwrite.Close();
}
else
{
lblerror.Text = "File Doesnt Exists";
}
Any Help will be Appreciated!

Can't get inner text from FQL using Xpath C#

I'm trying to get the inner text from a XML tag called sharecount, I need to get it by the normalized_url tag like so:
string ShareCount;
string Url = "'" + "http://www.example.com/Article.aspx?id=" + GuideID + "'";
XmlNode Node;
try
{
//return Share count (using Xpath).
XmlDoc.SelectSingleNode("fql_query_response/link_stat[normalized_url=" + Url + "]/share_count");
ShareCount = XmlDoc.InnerText;
int.TryParse(ShareCount, out Value); //Turn string to int.
return Value;
}
catch
{
return 0;
}
and this is the XML:
<fql_query_response xmlns="http://api.facebook.com/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" list="true">
<link_stat>
<url>http://www.example.com/Article.aspx?id=1909</url>
<normalized_url>http://www.example.com/Article.aspx?id=1909</normalized_url>
<share_count>11</share_count>
<like_count>3</like_count>
<comment_count>0</comment_count>
<total_count>14</total_count>
<commentsbox_count>8</commentsbox_count>
<comments_fbid>10150846665210566</comments_fbid>
<click_count>0</click_count>
</link_stat>
</fql_query_response>
<link_stat>
<url>http://www.example.com/Article.aspx?id=1989</url>
<normalized_url>http://www.example.com/Article.aspx?id=1989</normalized_url>
<share_count>11</share_count>
<like_count>3</like_count>
<comment_count>0</comment_count>
<total_count>14</total_count>
<commentsbox_count>8</commentsbox_count>
<comments_fbid>10150846665210566</comments_fbid>
<click_count>0</click_count>
</link_stat>
</fql_query_response>
The thing is i got in the return value: "www.example.com/Article.aspx?id=1132http://www.example.com/Article.aspx?id=190900000101502138970422760" what am i doing wrong? thanks!
The problem is you are Selecting a single node but obtaining the content from the root element. On top of that, you have a root namespace, therefore use of NameSpaceManger for searches is required. Try this sample:
string GuideID = "1989";
string Url = "'" + "http://www.example.com/Article.aspx?id=" + GuideID + "'";
var XmlDoc = new XmlDocument();
XmlDoc.Load(new FileStream("XMLFile1.xml",FileMode.Open,FileAccess.Read));
var nsm = new XmlNamespaceManager(XmlDoc.NameTable);
nsm.AddNamespace("s", "http://api.facebook.com/1.0/");
var node = XmlDoc.SelectSingleNode("s:fql_query_response/s:link_stat[s:normalized_url=" + Url + "]/s:share_count", nsm);
var ShareCount = node.InnerText;
And here is the LINQ way with same namespace manager and an XPathSelector.
XDocument xdoc = XDocument.Load(new FileStream("XMLFile1.xml", FileMode.Open, FileAccess.Read));
var lnode = xdoc.XPathSelectElements("s:fql_query_response/s:link_stat[s:normalized_url=" + Url + "]/s:share_count",nsm).First();
var ret = lnode.Value;

how to read and change the xml file in C#

Thank you very much for reading my question.
and this is my xml file. (for node Songs, many childNodes named Song)
<?xml version="1.0" encoding="utf-8" ?>
<xmlData>
<version>1.0</version>
<Songs>
<Song>
<artist>mic</artist>
<track>2</track>
<column>happy</column>
<date>14</date>
</Song>
<Song>
<artist>cool</artist>
<track>2</track>
<column>work</column>
<date>4</date>
</Song>
</Songs>
</xmlData>
reading xml, i use the following code:
XmlDocument doc = new XmlDocument();
doc.Load(xmlFilePath);
XmlNode versionNode = doc.SelectSingleNode(#"/xmlData/version");
Console.WriteLine(versionNode.Name + ":\t" + versionNode.InnerText);
XmlNode SongsNode = doc.SelectSingleNode(#"/xmlData/Songs");
Console.WriteLine(SongsNode.Name + "\n");
XmlDocument docSub = new XmlDocument();
docSub.LoadXml(SongsNode.OuterXml);
XmlNodeList SongList = docSub.SelectNodes(#"/Songs/Song");
if (SongList != null)
{
foreach (XmlNode SongNode in SongList)
{
XmlNode artistDetail = SongNode.SelectSingleNode("artist");
Console.WriteLine(artistDetail.Name + "\t: " + artistDetail.InnerText);
XmlNode trackDetail = SongNode.SelectSingleNode("track");
Console.WriteLine(trackDetail.Name + "\t: " + trackDetail.InnerText);
XmlNode columnDetail = SongNode.SelectSingleNode("column");
Console.WriteLine(columnDetail.Name + "\t: " + columnDetail.InnerText);
XmlNode dateDetail = SongNode.SelectSingleNode("date");
Console.WriteLine(dateDetail.Name + "\t: " + dateDetail.InnerText + "\n");
}
}
it seems working.
but how can i write the change to xml file?
maybe, i will change some childNode in Song, and may delete the whole chindNode by artist keyword.
is it possible such as this function
bool DeleteSongByArtist(string sArtist);
bool ChangeNodeInSong(string sArtist, string sNodeName, string value);
because the "Reading solution is "XmlDucoment", so it is better if "changing solution" by using "XmlDocument"
but, if you have better idea to read and change the xml file, please give me the sample code... and please don't write a name of solution such as "Ling to xml"...acutally, i do many testes, but failed.
Welcome to Stackoverflow!
You can change the nodes simply by setting a new .Value or in your case .InnerText.
Sample
// change the node
trackDetail.InnerText = "NewValue"
// save the document
doc.Save(xmlFilePath);
More Information
How To: Modify an Existing Xml File
MSDN - XmlDocument.Save Method
You need to use an XmlWriter. The easiest way to do it would be something like this...
using(XmlWriter writer = new XmlWriter(textWriter))
{
doc.WriteTo(writer);
}
Where textWriter is your initialized Text Writer.
Actually, forget that... the easiest way is to call...
doc.Save(xmlFilePath);
To delete an artist by artist name add the following method:
bool DeleteSongByArtist(XmlDocument doc, string artistName)
{
XmlNodeList SongList = doc.SelectNodes(#"/Songs/Song");
if (SongList != null)
{
for (int i = SongList.Count - 1; i >= 0; i--)
{
if (SongList[i]["artist"].InnerText == artistName && SongList[i].ParentNode != null)
{
SongList[i].ParentNode.RemoveChild(SongList[i]);
}
}
}
}
You probably want to clean it up a bit more to be more resilient. When you call it, change your initial code to be like this. Don't create the subDocument as you want to work with the entire XmlDocument.
XmlDocument doc = new XmlDocument();
doc.Load(xmlFilePath);
XmlNode versionNode = doc.SelectSingleNode(#"/xmlData/version");
Console.WriteLine(versionNode.Name + ":\t" + versionNode.InnerText);
XmlNode SongsNode = doc.SelectSingleNode(#"/xmlData/Songs");
Console.WriteLine(SongsNode.Name + "\n");
XmlNodeList SongList = doc.SelectNodes(#"/Songs/Song");
if (SongList != null)
{
foreach (XmlNode SongNode in SongList)
{
XmlNode artistDetail = SongNode.SelectSingleNode("artist");
Console.WriteLine(artistDetail.Name + "\t: " + artistDetail.InnerText);
XmlNode trackDetail = SongNode.SelectSingleNode("track");
Console.WriteLine(trackDetail.Name + "\t: " + trackDetail.InnerText);
XmlNode columnDetail = SongNode.SelectSingleNode("column");
Console.WriteLine(columnDetail.Name + "\t: " + columnDetail.InnerText);
XmlNode dateDetail = SongNode.SelectSingleNode("date");
Console.WriteLine(dateDetail.Name + "\t: " + dateDetail.InnerText + "\n");
}
}
You aren't able to save your changes because you made changes to an entirely new document!
You likely meant to do the following:
XmlNode SongsNode = doc.SelectSingleNode(#"/xmlData/Songs");
Console.WriteLine(SongsNode.Name + "\n");
// Don't make a new XmlDocument here! Use your existing one
XmlNodeList SongList = SongsNode.SelectNodes(#"/Song");
At this point SongList is still living inside doc. Now when you call:
doc.Save(xmlFilePath);
Your changes will be saved as you intended.
If you're looking to delete nodes that match certain criteria:
// Use XPath to find the matching node
XmlNode song = SongsNode.SelectSingleNode(#"/Song[artist='" + artist + "']");
// Remove it from its Parent
SongsNode.RemoveChild(song);
If you're looking to add a new node:
// Create the new nodes using doc
XmlNode newSong = doc.CreateElement("Song");
XmlNode artist = doc.CreateElement("artist");
artist.InnerText = "Hello";
// Begin the painstaking process of creation/appending
newSong.AppendChild(artist);
// rinse...repeat...
// Finally add the new song to the SongsNode
SongsNode.AppendChild(newSong);
You could do
XmlNodeList SongList = doc.SelectNodes(#"//Songs/Song");
The // tells it to select the Songs node anywhere in document. This is better than
doc.SelectNodes(#"/document/level1/music/Songs")
Note that the above statement is oviously not for your xml, but to prove a point about //
Using // removes the need for your docSub document and SongsNode element.
To add then delete a song, just use the following
XmlDocument doc = new XmlDocument();
XmlElement ea = doc.SelectSingleNode("//songs");
XmlElement el = doc.CreateElement("song");
XmlElement er;
ea.AppendChild(el);
//doing my work with ea
//you could use innerxml.
el.InnerXml = "<artist>Judas Priest</artist><track>7</track><column>good</column><date>1</date>";
//or you can treat each node as above
er = doc.CreateElement("Name");
el.AppendChild(er);
er.InnerText = "The Ripper";
//but you don't nead this song any more?
ea.RemoveChild(el);
//so it's gone.
And thats all there is to it.
please don't write a name of solution such as "Ling to xml"...acutally, i do many testes, but failed.
Still I think, this is a very good time to start to use Linq2Xml. If you don't like it, just ignore.
XDocument xDoc = XDocument.Load(new StringReader(xml));
//Load Songs
var songs = xDoc.Descendants("Song")
.Select(s => new
{
Artist = s.Element("artist").Value,
Track = s.Element("track").Value,
Column = s.Element("column").Value,
Date = s.Element("date").Value,
})
.ToArray();
//Delete Songs
string songByArtist="mic";
xDoc.Descendants("Song")
.Where(s => s.Element("artist").Value == songByArtist)
.Remove();
string newXml = xDoc.ToString();

Modifying InnerXml of a text XmlNode

I traverse an html document with SGML and XmlDocument. When I find an XmlNode which its type is Text, I need to change its value that has an xml element. I can't change InnerXml because it's readonly. I tried to change InnerText, but this time tag descriptor chars < and > encoded to < and >. for example:
<p>
This is a text that will be highlighted.
<anothertag />
<......>
</p>
I'm trying to change to:
<p>
This is a text that will be <span class="highlighted">highlighted</span>.
<anothertag />
<......>
</p>
What is the easiest way to modify the value of a text XmlNode?
I have a workaround, I don't know it is a real solution or what, but it can result what I want. Please comment for this code if it is worthy solution or not
private void traverse(ref XmlNode node)
{
XmlNode prevOldElement = null;
XmlNode prevNewElement = null;
var element = node.FirstChild;
do
{
if (prevNewElement != null && prevOldElement != null)
{
prevOldElement.ParentNode.ReplaceChild(prevNewElement, prevOldElement);
prevNewElement = null;
prevOldElement = null;
}
if (element.NodeType == XmlNodeType.Text)
{
var el = doc.CreateElement("text");
//Here is manuplation of the InnerXml.
el.InnerXml = element.Value.Replace(a_search_term, "<b>" + a_search_term + "</b>");
//I don't replace element right now, because element.NextSibling will be null.
//So I replace the new element after getting the next sibling.
prevNewElement = el;
prevOldElement = element;
}
else if (element.HasChildNodes)
traverse(ref element);
}
while ((element = element.NextSibling) != null);
if (prevNewElement != null && prevOldElement != null)
{
prevOldElement.ParentNode.ReplaceChild(prevNewElement, prevOldElement);
}
}
Also, I remove <text> and </text> strings after the traverse function:
doc = new XmlDocument();
doc.PreserveWhitespace = true;
doc.XmlResolver = null;
doc.Load(sgmlReader);
var html = doc.FirstChild;
traverse(ref html);
textBox1.Text = doc.OuterXml.Replace("<text>", String.Empty).Replace("</text>", String.Empty);
using System;
using System.Xml;
public class Sample {
public static void Main() {
XmlDocument doc = new XmlDocument();
doc.LoadXml(
"<p>" +
"This is a text that will be highlighted." +
"<br />" +
"<img />" +
"</p>");
string ImpossibleMark = "_*_";
XmlNode elem = doc.DocumentElement.FirstChild;
string thewWord ="highlighted";
if(elem.NodeType == XmlNodeType.Text){
string OriginalXml = elem.ParentNode.InnerXml;
while(OriginalXml.Contains(ImpossibleMark)) ImpossibleMark += ImpossibleMark;
elem.InnerText = elem.InnerText.Replace(thewWord, ImpossibleMark);
string replaceString = "<span class=\"highlighted\">" + thewWord + "</span>";
elem.ParentNode.InnerXml = elem.ParentNode.InnerXml.Replace(ImpossibleMark, replaceString);
}
Console.WriteLine(doc.DocumentElement.InnerXml);
}
}
The InnerText property will give you the text content of all the child nodes of the XmlNode. What you really want to set is the InnerXml property, which will be construed as XML, not as text.

Categories

Resources