I have two XML files that I need to compare for differences, the XML is very simple:
File 1:
<?xml version="1.0" encoding="utf-8"?>
<Feeds zone="my zone">
<Feed name="attribDump.json">ac1f07edc491a3d237cdfb1a17fc4551</Feed>
<Feed name="focus_GroupsKV.txt">0f9e0a14a4ffce6ff5065b6e088c1f84</Feed>
<Feed name="NAM_FORMATTED.csv">9e875496cdb072b5e54318d51295fdba</Feed>
<Feed name="BNP\activityTitles.txt">2d27c0f19b71b4b411bcb00011d3f8b0</Feed>
</Feeds>
and File 2:
<?xml version="1.0" encoding="utf-8"?>
<FeedsRequest version="1">
<Feeds zone="my zone">
<Feed name="attribDump.json">ac1f07edc491a3d237cdfb1a17fc4551</Feed>
<Feed name="focus_GroupsKV.txt">0f9e0a14a4ffce6ff5065b6e088c1f84</Feed>
<Feed name="BNP\activityTitles.txt">e54c5b851ee3ff3f43b10d24f2316431</Feed>
</Feeds>
</FeedsRequest>
File 1 is an inventory list of files on our file share and File 2 is used by a disconnected device that will need to be refreshed from File 1. The checks I need to make are 1) make sure all of the feeds in File 1 are in File 2 and 2) make sure any feeds that are found have the same hashCode(the long character string). Once the checks have been completed, I need to create a response file that has a list of all of the feeds and then an attribute on each one that designates ok (file was found and matched), missing (file wasn't found), or updated (file was found but it was an older version).
So basically the result file would look as such:
<?xml version="1.0" encoding="utf-8"?>
<FeedsResponse version="1">
<Feeds zone="my zone">
<Feed name="attribDump.json" status="ok">ac1f07edc491a3d237cdfb1a17fc4551</Feed>
<Feed name="focus_GroupsKV.txt" status="ok">0f9e0a14a4ffce6ff5065b6e088c1f84</Feed>
<Feed name="NAM_FORMATTED.csv" status="missing">afd2c620053ed4f85ab02b4cc5f7a2b2</Feed>
<Feed name="BNP\activityTitles.txt" status="updated">90805b851ee3ff3f43b10d24f2316431</Feed>
What I'm doing currently is looping through all of the files in File 1, then checking them against File 2 for differences. Where I'm stuck, been a while since I've worked with XML, is how to build out the response document.
FileInfo feedList = new FileInfo(_feedList);
FileInfo feedRequest = new FileInfo(_feedRequest);
// Load the documents
XmlDocument feedListXmlDoc = new XmlDocument();
feedListXmlDoc.Load(_feedList);
// Load the documents
XmlDocument feedRequestXmlDoc = new XmlDocument();
feedRequestXmlDoc.Load(_feedRequest);
//create response doc
XmlDocument feedResponseXmlDoc = new XmlDocument();
// Define a single node
XmlNode feedListNode;
XmlNode feedRequestNode;
// Get the root Xml element
XmlElement feedListRoot = feedListXmlDoc.DocumentElement;
XmlElement feedRequestRoot = feedRequestXmlDoc.DocumentElement;
// Get a list of all player names
XmlNodeList feedListXml = feedListRoot.GetElementsByTagName("Feed");
XmlNodeList feedRequestXml = feedRequestRoot.GetElementsByTagName("Feed");
// Create an XmlWriterSettings object with the correct options.
XmlWriter writer = null;
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = (" ");
settings.OmitXmlDeclaration = false;
// Create the XmlWriter object and write some content.
writer = XmlWriter.Create(_resultPath, settings);
writer.WriteStartElement("FeedsDiff");
// The compare algorithm
bool feedMatch = false;
int j = 0;
try
{
// loop through list of current feeds
for (int i = 0; i < feedListXml.Count; i++)
{
feedListNode = feedListXml.Item(i);
string feedListName = feedListNode.Attributes["name"].Value.ToString();
string feedListHash = feedListXml.Item(i).InnerText.ToString();
//check feed request list for a match
while (j < feedRequestXml.Count && feedMatch == false)
{
feedRequestNode = feedRequestXml.Item(j);
string feedRequestName = feedRequestNode.Attributes["name"].Value.ToString();
//checks to see if feed names match
if (feedListName == feedRequestName)
{
feedMatch = true;
string feedRequestHash = feedRequestXml.Item(j).InnerText.ToString();
//since we found the node, we can remove it from the request list
XmlNode node = feedRequestNode.ParentNode;
node.RemoveChild(feedRequestNode);
//checks to see if hash codes match
if (feedListHash == feedRequestHash)
{
//if name and code match, move to the next one
feedMatch = true;
//add 'status="ok"' attribute to the node
//feedResponseXmlDoc.ImportNode(feedRequestNode,false);
Debug.WriteLine(feedListName + " name and hash match");
j = 0;
}
else
{
feedMatch = true;
//feed has been updated since last device sync
//need to add status='update' attribute and append file to response
Debug.WriteLine(feedListName + " name matched but hash did not");
}
}
else
{
//names didn't match
//add status="missing" to the node
j++;
}
}
feedMatch = false;
}
// end Xml document
writer.WriteEndElement();
writer.Flush();
}
finally
{
if (writer != null)
writer.Close();
}
Right now I'm trying to instantiate the response doc before the loop and then just add the elements as they are found but I'm having a hard time finding a concise way to do it. Any help is appreciated.
Take a look at differ, from my open-source project CodeBlocks over on CodePlex, it was designed for situations such as this one. It is also available on Nuget as "differ"
I figured it out:
public void CompareXml(string _feedList, string _feedRequest, string _resultPath)
{
FileInfo feedList = new FileInfo(_feedList);
FileInfo feedRequest = new FileInfo(_feedRequest);
// Load the documents
XmlDocument feedListXmlDoc = new XmlDocument();
feedListXmlDoc.Load(_feedList);
// Load the documents
XmlDocument feedRequestXmlDoc = new XmlDocument();
feedRequestXmlDoc.Load(_feedRequest);
// Define a single node
XmlNode feedListNode;
XmlNode feedRequestNode;
// Get the root Xml element
XmlElement feedListRoot = feedListXmlDoc.DocumentElement;
XmlElement feedRequestRoot = feedRequestXmlDoc.DocumentElement;
// Get a list of feeds for the stored list and the request
XmlNodeList feedListXml = feedListRoot.GetElementsByTagName("Feed");
XmlNodeList feedRequestXml = feedRequestRoot.GetElementsByTagName("Feed");
bool feedLocated = false;
int j = 0;
try
{
// loop through list of current feeds
for (int i = 0; i < feedListXml.Count; i++)
{
feedListNode = feedListXml.Item(i);
//create status attribute
XmlAttribute attr = feedListXmlDoc.CreateAttribute("status");
string feedListName = feedListNode.Attributes["name"].Value.ToString();
string feedListHash = feedListXml.Item(i).InnerText.ToString();
//check feed request list for a match
while (j < feedRequestXml.Count && feedLocated == false)
{
feedRequestNode = feedRequestXml.Item(j);
string feedRequestName = feedRequestNode.Attributes["name"].Value.ToString();
//checks to see if feed names match
if (feedRequestName == feedListName)
{
string feedRequestHash = feedRequestXml.Item(j).InnerText.ToString();
//checks to see if hashCodes match
if (feedListHash == feedRequestHash)
{
//if name and code match, set status to ok
attr.Value = "ok";
Debug.WriteLine(feedListName + " name and hash match. Status: 'ok'");
}
else
{
//if hashCodes don't match, set status attribute to updated
attr.Value = "updated";
Debug.WriteLine(feedListName + " name matched but hash did not. Status: 'updated'");
}
feedListNode.Attributes.Append(attr);
feedLocated = true;
}
else
{
//names didn't match, checking to see if we're at the end of the request list
if (j + 1 == feedRequestXml.Count)
{
//file name wasn't found in the request list, set status attribute to missing
attr.Value = "missing";
feedListNode.Attributes.Append(attr);
feedLocated = true;
j = 0;
Debug.WriteLine("Reached the end of the file request list without a match. Status: 'missing'");
}
//file name wasn't located on this pass, move to next record
j++;
}
}
feedLocated = false;
}
}
finally
{
Debug.WriteLine("Result file has been written out at " + _resultPath);
}
feedListXmlDoc.Save(_resultPath);
}
Related
So, I am trying to make a "Recent Files" feature in my WPF application by use of C#'s XML class.
Basically, the program needs to read each node and get the URI of file to generate menu item.
Problem is, r.Value doesn't give me a string to work with and I'm starting to question whether or not this code is going to work.
XmlTextReader r = new XmlTextReader(this.PathToSpecialFolder + #"\" + Application.CompanyName + #"\" + Application.ProductName + #"\Recent.xml");
int count = 0;
while (r.Read())
{
switch (r.NodeType)
{
case XmlNodeType.Element:
if (r.Name == "Path")
{
count++;
if (count <= 5)
{
MenuItem m = new MenuItem() { Header = r.Value };
openRecentMenuItem.Items.Add(m);
//r.Value populates nothing
}
}
break;
}
}
r.Close();
On a related topic, I'm also trying to get it to where the XML file will only store five nodes. I want to...
Get number of nodes (which I have already accomplished)
Check whether new node + node count > 5 and, if so, prepend new node and remove last node. Otherwise, just add new node
How do you remove the last node? Here is my XML file structure:
<?xml version="1.0" encoding="utf-8"?>
<RecentFiles>
<File>
<Path>Path goes here.</Path>
</File>
</RecentFiles>
UPDATE:
The code below solves my issue with reading (as per advice of John), however, I still cannot figure out how to remove the last node when writing. I prefer not to use Linq in this case, so a solution using XmlReader is preferred, thanks.
XmlReader x = XmlReader.Create(this.PathToSpecialFolder + #"\" + Application.CompanyName + #"\" + Application.ProductName + #"\Recent.xml");
int c = 0;
while (x.Read())
{
if (x.NodeType == XmlNodeType.Element && x.Name == "Path")
{
c++;
if (c <= 10)
{
MenuItem m = new MenuItem() { Header = x.ReadInnerXml() };
m.Click += delegate
{
};
openRecentMenuItem.Items.Add(m);
}
}
}
x.Close();
}
I have XML which is in project folder. I am loading the contents using this
XmlDocument doc = new XmlDocument();
string testFilesLocation = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
string dataSource;
string xmlFileName = "Claim.txt";
if (System.IO.Directory.Exists(testFilesLocation + #"\Resources"))
{
dataSource = testFilesLocation + #"\Resources\" + xmlFileName;
}
else
{
dataSource = testFilesLocation + #"\" + xmlFileName;
}
doc.Load(dataSource);
XML has following nodes
<ClaimKeyInfo>
<CompanyID>XXXX</CompanyID>
<ClaimNum>XX-XXXXX-XX<ClaimNum>
</ClaimKeyInfo>
<ClaimInfo>
<ClaimNum>XX-XXXXX-XX</ClaimNum>
<ClaimSensitivityInd>N</ClaimSensitivityInd>
<ClaimStatus>Open</ClaimStatus>
<ClaimInfo>
I am doing this to get ClaimNum elements
XmlElement root = doc.DocumentElement;
XmlNodeList elemList = root.GetElementsByTagName("ClaimNum");
for (int i = 0; i< elemList.Count; i++)
{
elemList[i].InnerXml = "YY-YYYYY-YY";
doc.Save(dataSource);
}
I do get both the elements in elemList but I am not able to change the values inside it.
Any help would be appreciated.
You can use LINQ to XML for that.
var xDoc = XDocument.Load("filePath");
var numbers = xDoc.Descendants("ClaimNum");
foreach(var number in numbers)
{
// do your work with numbers
number.Value = "123456";
}
xDoc.Save("filePath");
Here, the numbers are contains XElements which is your ClaimNums.You can change the Value property and save the XML file.If you want to get specific numbers you can use Where extension method.If you want more details see these documentations:
How to: Find an Element with a Specific Attribute
How to: Find an Element with a Specific Child Element
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();
Trying to create dynamic XML file and save the data to it, however i am able to save the last entry and not all the entries. Any thoughts?
To generate XML file with needed nodes:
GenerateXML()
XmlDocument doc = new XmlDocument();
XmlNode docNode = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
doc.AppendChild(docNode);
XmlNode albums = doc.CreateElement("albums");
doc.AppendChild(albums);
XmlNode album = doc.CreateElement("album");
albums.AppendChild(album);
XmlNode title = doc.CreateElement("title");
albums.AppendChild(title);
doc.Save(System.Windows.Forms.Application.StartupPath + "\\albums.xml");
The output from button click gives me either 10 records or 100 records or some number of records:
//album is a class returned by web service
if (Elements.Count > 0)
{
string path = System.Windows.Forms.Application.StartupPath + "\\albums.xml";
//Read the file stream in read mode
FileStream READER = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
System.Xml.XmlDocument albumSpecs = new System.Xml.XmlDocument();
albumSpecs.Load(READER);
FileStream WRITER = null;
GenerateXML();
//Create a FileStream for writing
WRITER = new FileStream(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite);
XmlElement root = doc.DocumentElement;
XmlNodeList nodes = root.SelectNodes("//albums");
for (int i = 0; i < ResultsList.Items.Count - 1; i++)
{
album = (Album)ResultsList.Items[i];
foreach (XmlNode node in nodes)
{
node["title"].InnerText = album.Title;
}
}
//Write the data to the filestream
doc.Save(WRITER);
}
I get the last entry data in the XML and not all the entries returned.
<?xml version="1.0" encoding="UTF-8"?>
<albums>
<album />
<title>She will be loved</title>
</albums>
How do i build run time XML based on the # of entries returned?
First load already saved xml into XmlDocument and then add new elements into it ...
GenerateXML()
string xmlFilePath = Path.Combine(System.Windows.Forms.Application.StartupPath, "albums.xml");
XmlDocument doc = new XmlDocument();
XmlNode albums = null;
if (!File.Exists(xmlFilePath))
{
XmlNode docNode = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
doc.AppendChild(docNode);
albums = doc.CreateElement("albums");
doc.AppendChild(albums);
}
else
{
doc.Load(xmlFilePath);
albums = doc.ChildNodes[1];
}
XmlNode album = doc.CreateElement("album");
albums.AppendChild(album);
XmlNode albumName = doc.CreateElement("name");
album.AppendChild(albumName);
XmlNode title = doc.CreateElement("title");
album.AppendChild(title);
doc.Save(xmlFilePath);
Try adding elements to your XML document ?
for (int i = 0; i < ResultsList.Items.Count - 1; i++)
{
album = (Album)ResultsList.Items[i];
foreach (XmlNode node in nodes)
{
node["title"].InnerText = album.Title;
}
}
This sample of code isnt doing much, because you only have one element to iterate on ...
Edit :
I imagine you want to do something like this :
for(int i = 0; i < ResultsList.Items.count -1; i++)
{
XmlNode child = doc.createElement("Title");
doc.AppendChild .....
}
doc.save;
I am writing a prototype to programmatically get video data from a database and use C# to place this data into an XML manifest file. Each video is an asset element inside XML, and I am querying all the data I need from a SQLCommand.
Problem: The XML file can only hold up to 100 video assets per file, and so I need to come up with an iterator that saves 100 assets per file until I reach the end row in the database. I am using while(r.Read()), a SqlDataReader to go into the data base using a SqlCommand.
I would like to know how to go about this process of appending 100 assets per file using a certain SQLCommand and an iteration inside the reader to process all files needed.
Below is my code thus far! (Clearly I am going to have to change some things around, such as the global elements of the XML file, which need to go in every XML file created)
protected void btnExecute_Click(object sender, EventArgs e)
{
//On btn click, call method to execute program and save all files in local path
GetVideoData();
}
protected void GetVideoData()
{
string preparer = "AgencyOasis";
string type = "VIDEO_FULL";
XmlDocument doc = new XmlDocument();
XmlNode docNode = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
doc.AppendChild(docNode);
//add global elements:
//create parameters for each attribute in root to be replaced, and append the new root tag to empty XML doc
XmlElement root = doc.CreateElement("publisher-upload-manifest");
root.SetAttribute("publisher-id", tbPublisherID.Text);
root.SetAttribute("preparer", preparer);
doc.AppendChild(root);
//append notify as child to root, already in doc
if (!string.IsNullOrEmpty(tbEmail.Text)) {
XmlElement notify = doc.CreateElement("notify");
notify.SetAttribute("email", tbEmail.Text);
root.AppendChild(notify);
}
//THE REST OF THE ELEMENTS ARE A UNIQUE CASE FOR EACH VIDEO, THEREFORE SHOULD LOOP INSIDE THE QUERY RESULT SET, PER VIDEO.
string sql100 = "SELECT TOP 100 Location, VideoLibraryId, Title, Keywords, IsActive, Description FROM VideoLibrary";
const string _connStringName = "SanfordVideosConnectionString";
string dsn = ConfigurationManager.ConnectionStrings[_connStringName].ConnectionString;
using (SqlConnection conn = new SqlConnection(dsn))
using (SqlCommand cmd = new SqlCommand(sql100, conn))
{
conn.Open();
SqlDataReader r = cmd.ExecuteReader();
while (r.Read())
{
//while going through each row with above SQL command, set data to element attributes in doc for each asset
XmlElement asset = doc.CreateElement("asset");
asset.SetAttribute("filename", r["Location"].ToString());
asset.SetAttribute("refid", r["VideoLibraryId"].ToString());
// TODO: NEED ACTUAL FILE LOCATION BEFORE I CAN EXTRACT THE SIZE OF THE FILE ITSELF
/*create new FileInfo object to get length of existing video file
FileInfo f = new FileInfo(r["Location"].ToString());
long f1 = f.Length; */
//asset.SetAttribute("size", "10"); // TODO: f1.ToString() for static value of 2nd #
// TODO: NEED ACTUAL FILE LOCATION AGAIN FOR HASH-CODE
//asset.SetAttribute("hash-code", "10"); //TODO: GetMD5Hash(r["Location"].ToString())
//setting the type globally for all videos to VIDEO_FULL ensures FLV and MP4 formats
asset.SetAttribute("type", type);
root.AppendChild(asset);
XmlElement title = doc.CreateElement("title");
title.SetAttribute("name", r["Title"].ToString());
title.SetAttribute("refid", r["VideoLibraryId"].ToString());
title.SetAttribute("active", r["IsActive"].ToString().ToUpper());
// TODO: CHECK TO SEE IF VIDEO-FULL-REFID IS CORRECT
//title.SetAttribute("video-full-refid", r["Location"].ToString() + "-" + r["VideoLibraryId"].ToString());
XmlElement shortDesc = doc.CreateElement("short-description");
shortDesc.InnerText = GetTrimmedDescription(250, r["Description"].ToString());
title.AppendChild(shortDesc);
root.AppendChild(title);
XmlElement longDesc = doc.CreateElement("long-description");
longDesc.InnerText = GetTrimmedDescription(5000, r["Description"].ToString());
title.AppendChild(longDesc);
root.AppendChild(title);
}
}
//TEMPORARY FILE SAVE LOCATION TODO: SAVE MULTIPLE FILES IN LOCAL FOLDER
//returns the directory from where the current application domain was loaded
//string xmlPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, inputFileName1);
string xmlPath = Server.MapPath(txtBoxInput.Text);
XmlTextWriter writer = new XmlTextWriter(xmlPath, null);
writer.Formatting = Formatting.Indented;
doc.Save(xmlPath);
}
//Trims long and short descriptions to max size of chars depending on max size (250 for short and 5000 for long)
public string GetTrimmedDescription(int maxLength, string desc) {
if (desc.Length > maxLength)
{
return desc.Substring(0, (maxLength - 4)) + " ...";
}
else
{
return desc;
}
}
Feel free to ask me any questions about the program and I'll try my best to explain!
If I keep your code mostly as is, here is a possible solution. I have added in some comments where I refactored pieces.
The general idea is to fetch everything (not just 100 records) and introduce a counter that tracks where you are at. Every 100 records you spit out the file and reset. After you've read everything you have to spit out what is remaining. Since there are multiple files created, you will also have to track that and use a different filename. I just added a file suffix. I think the code below should work for you.
protected void btnExecute_Click(object sender, EventArgs e)
{
//On btn click, call method to execute program and save all files in local path
GetVideoData();
}
protected void GetVideoData()
{
string type = "VIDEO_FULL";
XmlDocument doc;
XmlElement root;
// Refactor document creation to its own function
doc = CreateANewDoc();
// Refactor root creation to its own function
root = CreateANewRoot(doc);
//THE REST OF THE ELEMENTS ARE A UNIQUE CASE FOR EACH VIDEO, THEREFORE SHOULD LOOP INSIDE THE QUERY RESULT SET, PER VIDEO.
// remove the top 100 and fetch everything
string sql100 = "SELECT Location, VideoLibraryId, Title, Keywords, IsActive, Description FROM VideoLibrary";
const string _connStringName = "SanfordVideosConnectionString";
string dsn = ConfigurationManager.ConnectionStrings[_connStringName].ConnectionString;
using (SqlConnection conn = new SqlConnection(dsn))
using (SqlCommand cmd = new SqlCommand(sql100, conn))
{
conn.Open();
SqlDataReader r = cmd.ExecuteReader();
// declare some counters
int counter = 0;
int filecounter = 0;
while (r.Read())
{
counter++; // Increment counter
// when you hit 100, write stuff out and reset
if (counter == 100 )
{
filecounter++; // Increment filecounter
DumpOutFile(doc, filecounter);
// Reset counter
counter = 0;
// Reset doc
doc = CreateANewDoc();
root = CreateANewRoot(doc);
}
//while going through each row with above SQL command, set data to element attributes in doc for each asset
XmlElement asset = doc.CreateElement("asset");
asset.SetAttribute("filename", r["Location"].ToString());
asset.SetAttribute("refid", r["VideoLibraryId"].ToString());
// TODO: NEED ACTUAL FILE LOCATION BEFORE I CAN EXTRACT THE SIZE OF THE FILE ITSELF
/*create new FileInfo object to get length of existing video file
FileInfo f = new FileInfo(r["Location"].ToString());
long f1 = f.Length; */
//asset.SetAttribute("size", "10"); // TODO: f1.ToString() for static value of 2nd #
// TODO: NEED ACTUAL FILE LOCATION AGAIN FOR HASH-CODE
//asset.SetAttribute("hash-code", "10"); //TODO: GetMD5Hash(r["Location"].ToString())
//setting the type globally for all videos to VIDEO_FULL ensures FLV and MP4 formats
asset.SetAttribute("type", type);
root.AppendChild(asset);
XmlElement title = doc.CreateElement("title");
title.SetAttribute("name", r["Title"].ToString());
title.SetAttribute("refid", r["VideoLibraryId"].ToString());
title.SetAttribute("active", r["IsActive"].ToString().ToUpper());
// TODO: CHECK TO SEE IF VIDEO-FULL-REFID IS CORRECT
//title.SetAttribute("video-full-refid", r["Location"].ToString() + "-" + r["VideoLibraryId"].ToString());
XmlElement shortDesc = doc.CreateElement("short-description");
shortDesc.InnerText = GetTrimmedDescription(250, r["Description"].ToString());
title.AppendChild(shortDesc);
root.AppendChild(title);
XmlElement longDesc = doc.CreateElement("long-description");
longDesc.InnerText = GetTrimmedDescription(5000, r["Description"].ToString());
title.AppendChild(longDesc);
root.AppendChild(title);
}
// Dump out whatever is left (handles the remainder after division by 100
DumpOutFile(doc, filecounter + 1);
}
}
//Trims long and short descriptions to max size of chars depending on max size (250 for short and 5000 for long)
public string GetTrimmedDescription(int maxLength, string desc) {
if (desc.Length > maxLength)
{
return desc.Substring(0, (maxLength - 4)) + " ...";
}
else
{
return desc;
}
}
private XmlDocument CreateANewDoc()
{
XmlDocument doc = new XmlDocument();
XmlNode docNode = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
doc.AppendChild(docNode);
return doc;
}
private XmlElement CreateANewRoot(XmlDocument doc)
{
string preparer = "AgencyOasis";
//add global elements:
//create parameters for each attribute in root to be replaced, and append the new root tag to empty XML doc
XmlElement root = doc.CreateElement("publisher-upload-manifest");
//root.SetAttribute("publisher-id", tbPublisherID.Text);
root.SetAttribute("publisher-id", "tbPublisherID.Text");
root.SetAttribute("preparer", preparer);
doc.AppendChild(root);
//append notify as child to root, already in doc
if (!string.IsNullOrEmpty(tbEmail.Text)) {
XmlElement notify = doc.CreateElement("notify");
notify.SetAttribute("email", tbEmail.Text);
root.AppendChild(notify);
}
return root;
}
private void DumpOutFile(XmlDocument doc, int filenumber)
{
//TEMPORARY FILE SAVE LOCATION TODO: SAVE MULTIPLE FILES IN LOCAL FOLDER
//returns the directory from where the current application domain was loaded
//string xmlPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, inputFileName1);
string xmlPath = Server.MapPath(txtBoxInput.Text + filenumber.ToString());
XmlTextWriter writer = new XmlTextWriter(xmlPath, null);
writer.Formatting = Formatting.Indented;
doc.Save(xmlPath);
}