Complicated XML Diff - c#
I have been doing a lot of research on various ways to compare directories of XML files to each other, such that each "actual build" XML file has a matching "template build" XML file. These templates are going to be the actual config files for future builds, so I need to go back through the currently working config files and check for differences in data. These differences will be included as client changeable configurations for the future builds.
I have looked at the XML Diff and Patch (both GUI and VisStu forms) and tried to get differences, but it returns exceptions left and right and can never get the diffGram created. It seems that the XD&P is looking for library elements that no longer exist or have been changed in ways that break it.
Now, I am brand new to XML and LINQ, but I know this is where my answer lies. I have been thinking about creating path strings for every line such that the following xml file:
<configuration>
<title>#ClientOfficialName# Interactive Map</title>
<subtitle>Powered By Yada</subtitle>
<logo>assets/images/mainpageglobe.png</logo>
<style alpha="0.9">
<colors>0xffffff,0x777777,0x555555,0x333333,0xffffff</colors>
<font name="Verdana"/>
<titlefont name="Verdana"/>
<subtitlefont name="Verdana"/>
</style>
Would create strings like:
configuration/title/"#ClientOfficialName# Interactive Map"
configuration/subtitle/"Powered By Yada"
configuration/logo/"assets/iamges/mainpageglobe.png"
configuration/style/alpha/"0.9"
configuration/style/colors/"0xffffff,0x777777,0x555555,0x333333,0xffffff"
and so on like this.
In this manner, I can get every line from the Actual and the Template files and compare them based on "if they are of the same node path, then compare text. If text of all exact siblings does not match, put string into differenceOutput.txt".
So far this is the best concept I have come up with. If anyone can help me achieve this (through this or any other method), I would greatly appreciate it.
I currently have the directory system working no problem, I just have no idea where to start with the population of the string containers from the xml file:
static void Main(string[] args)
{
//Set Up File Paths
var actualBuildPath = #"C:\actual";
var templateBuildPath = #"C:\template";
//Output File Setups
var missingFileList = new List<string>();
var differenceList = new List<string>();
//Iterate through Template Directory checking to see if the Current Build Directory
//has everything and finding differences if they exist
foreach (var filePath in Directory.GetFiles(templateBuildPath, "*.xml", SearchOption.AllDirectories))
{
//Announce Current File
Console.WriteLine("File: {0} ", filePath);
//Make Sure file Exists in current build
if (File.Exists(filePath.Replace(templateBuildPath, actualBuildPath)))
{
//Fill in String Containers as prep for comparison
var templateBuildFormattedXmlLines = PopulateStringContainerFromXML(filePath);
var actualBuildFormattedXmlLines = PopulateStringContainerFromXML(filePath.Replace(templateBuildPath, actualBuildPath));
//COMPARISON SECTION-------------------------------------------------------
xmlFileCompare(templateBuildFormattedXmlLines, actualBuildFormattedXmlLines);
}
//Put missing file into missing file output file
else
missingFileList.Add("Missing: " + filePath.Replace(templateBuildPath, actualBuildPath));
}
//Create Output Folder and Output Files
if (!Directory.Exists(actualBuildPath + #"\Outputs"))
Directory.CreateDirectory(actualBuildPath + #"\Outputs");
File.WriteAllLines(actualBuildPath + #"\Outputs\MissingFiles.txt", missingFileList);
File.WriteAllLines(actualBuildPath + #"\Outputs\differenceList.txt", differenceList);
//Wait to close console until user interacts
Console.ReadLine();
}
Assuming all the configuration files are the same (grammatically) I would recomend to read them into a object and compare these objects, with this you have the possibility to make a finer grained comparison e.g the subtitle may be left out of the comparison.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace XMLTest
{
class Program
{
static void Main(string[] args)
{
//You can use the XDocument.Load() Method to load a xml from a file path rather than a string
string xml = "<configuration><title>#ClientOfficialName# Interactive Map</title><subtitle>Powered By Yada</subtitle><logo>assets/images/mainpageglobe.png</logo><style alpha=\"0.9\"> <colors>0xffffff,0x777777,0x555555,0x333333,0xffffff</colors> <font name=\"Verdana\"/> <titlefont name=\"Verdana\"/> <subtitlefont name=\"Verdana\"/></style></configuration>";
XDocument d = XDocument.Parse(xml);
Configuration c = new Configuration();
c.Title = d.Descendants().Where(x => x.Name == "title").FirstOrDefault().Value;
c.SubTitle = d.Descendants().Where(x => x.Name == "subtitle").FirstOrDefault().Value;
c.Logo = d.Descendants().Where(x => x.Name == "logo").FirstOrDefault().Value;
Configuration.Style s = new Configuration.Style();
s.Alpha = (from attr in d.Descendants().Attributes() select attr).Where(x => x.Name == "alpha").FirstOrDefault().Value;
string tmp = d.Descendants().Where(x => x.Name == "colors").FirstOrDefault().Value;
foreach (string str in tmp.Split(','))
{
s.Colors.Add(Convert.ToInt32(str, 16));
}
s.FontName = (from attr in d.Descendants().Where(x=>x.Name =="font").Attributes() select attr).Where(x => x.Name == "name").FirstOrDefault().Value;
s.TitleFontName = (from attr in d.Descendants().Where(x => x.Name == "titlefont").Attributes() select attr).Where(x => x.Name == "name").FirstOrDefault().Value;
s.SubtitleFontName = (from attr in d.Descendants().Where(x => x.Name == "subtitlefont").Attributes() select attr).Where(x => x.Name == "name").FirstOrDefault().Value;
c.MyStyle = s;
Console.WriteLine(c.ToString());
Console.ReadKey();
}
}
public class Configuration : IComparable
{
public string Title;
public string SubTitle;
public string Logo;
public Style MyStyle;
public override string ToString()
{
return string.Format("Configuration : Title: {0}, Subtitle {1}, Logo {2}, Style: {3}",Title,SubTitle,Logo,MyStyle.ToString());
}
public class Style
{
public string Alpha;
public List<int> Colors = new List<int>();
public string FontName;
public string TitleFontName;
public string SubtitleFontName;
public override string ToString()
{
string s = "Alpha :" +Alpha;
s+= ", Colors: ";
foreach(int i in Colors){
s += string.Format("{0:x},",i);
}
s += " FontName :" + FontName;
s += " TitleFontName :" + TitleFontName;
s += " SubTitleFontName :" + SubtitleFontName;
return s;
}
}
public int CompareTo(object obj)
{
if ((obj as Configuration) == null)
{
throw new ArgumentException("Not instance of configuration");
}
//Very simple comparison, ranks by the title in the comparison object, here you could compare all the other values e.g Subtitle , logo and such to test if two instances are Equal
return String.Compare(this.Title, ((Configuration)obj).Title, true);
}
}
}
For a more Complete overview of Implementing Comparison see: https://msdn.microsoft.com/en-us/library/system.icomparable.compareto%28v=vs.110%29.aspx
Related
How to check if a string is in an array
I am trying to check whether a string is in an array and if continues even though the fileInfo.Name.Contains a string that is in files.Any: \\FILES LIKE DATABASE.MDB IS IN C:PROJECTS\HOLON\DATABASE.MDB **if (files.Any((fileInfo.Name.Contains)))** \\DO SOMETHING Console.WriteLine( fileInfo.Name, fileInfo.Length,
If you alread have the filenames collected in an array, then you should either do it this way: if (files.Any() && files.Contains(fileInfo.Name)) { // Do something } If you just want to check if a file exists then you can use File.Exists: if(System.IO.File.Exists(fileInfo.Name)) { // Do Something }
So you have a collection of full file paths? And you want to check if one or more of those list entries match with a specific file name? Perhaps this would work for you: string fileToSearch = "DATABASE.MDB"; bool found = files.Any(fileName => new FileInfo(fileName).Name.ToUpper() == fileToSearch.ToUpper()); Edit: An alternative to constructing new FileInfo objects would be to use System.IO.Path: bool found = files.Any(fileName => Path.GetFileName(fileName).ToUpper() == fileToSearch.ToUpper()); Edit 2: On the other hand, if you want to search for a specific file name, and you want to use the result, you could do something like this: var fileToSearch = "DATABASE.MDB"; var fileInfo = (from f in files let fi = new FileInfo(f) where fi.Name.ToUpper() == fileToSearch.ToUpper() select fi).FirstOrDefault(); if (fileInfo != null) { if (fileInfo.Exists) { Console.WriteLine($"{fileInfo.Name} ({fileInfo.Length} bytes)."); } else { Console.WriteLine($"{fileInfo.Name} (does not exist)."); } } I used a LINQ query here for readability. You could use the extension methods (files.Select(f => new FileInfo(f)).Where(fi => fi.Name.ToUpper() == fileToSearch.ToUpper()).FirstOrDefault()) as well, but that's up to you.
if (Array.Exists(files, element => element.Contains(fileInfo.Name)))
Getting list of Variables of map in BPM Metastorm
I'm trying to get list of variables in some map OUTSIDE program automatically. I know I can find them in .process file, with has xml structure. I also figured out that "x:object" with variable contains "x:Type" ending with "MboField}". But unfortunately I need to narrow searching criterias more, because I still can't find the main patern to separate variables from other objects. This is my current code in c#: var xdoc = XDocument.Load(patches.ProcessFilePatch); var xmlns = XNamespace.Get("http://schema.metastorm.com/Metastorm.Common.Markup"); IEnumerable<string> values = from x in xdoc.Descendants(xmlns+"Object") where x.Attribute(xmlns+"Type").Value.ToString().EndsWith("MboField}") select x.Attribute(xmlns+"Name").Value.ToString(); VariablesInProcessFile = values.ToList(); Any other ways to find Variables among others?
private void getVariablesInProcessFile() { var xdoc = XDocument.Load(patches.ProcessFilePatch); var xmlns = XNamespace.Get("http://schema.metastorm.com/Metastorm.Common.Markup"); var dane = xdoc.Descendants(xmlns + "Object").Where(x => CheckAttributes(x, xmlns)).ToArray(); IEnumerable<string> valuesE = from x in dane.Descendants(xmlns + "Object") where x.Attribute(xmlns + "Type").Value.ToString().EndsWith("MboField}") select x.Attribute(xmlns + "Name").Value.ToString(); VariablesInProcessFile = valuesE.ToList(); } private bool CheckAttributes(XElement x, XNamespace xmlns) { var wynik = x.Attribute(xmlns + "Name"); return wynik != null && (wynik.Value == patches.MapName + "Data" || wynik.Value == patches.altMapName + "Data"); } Where "patches" is my own class containing patch to .process file and possible names of group of Variables, usually related to name of the map.
How to extract specific data from XML data
I am using the following code snippet to parse and convert some XML data to CSV. I can convert the entire XML data and dump it into a file, however my requirements have changed and now I'm confused. public void xmlToCSVfiltered(string p, int e) { string all_lines1 = File.ReadAllText(p); all_lines1 = "<Root>" + all_lines1 + "</Root>"; XmlDocument doc_all = new XmlDocument(); doc_all.LoadXml(all_lines1); StreamWriter write_all = new StreamWriter(FILENAME2); XmlNodeList rows_all = doc_all.GetElementsByTagName("XML"); List<string[]> filtered = new List<string[]>(); foreach (XmlNode rowtemp in rows_all) { List<string> children_all = new List<string>(); foreach (XmlNode childtemp in rowtemp.ChildNodes) { children_all.Add(Regex.Replace(childtemp.InnerText, "\\s+", " ")); // <------- Fixed the Bug , Advisories dont span } string.Join(",", children_all.ToArray()); //write_all.WriteLine(string.Join(",", children_all.ToArray())); if (children_all.Contains(e.toString())) { filtered.Add(children_all.ToArray()); write_all.WriteLine(children_all); } } write_all.Flush(); write_all.Close(); foreach (var res in filtered) { Console.WriteLine(string.Join(",", res)); } } My input looks something like the following... My objective now is to only convert those "events" and compile into a CSV which have a certain number. Lets say, for example, I only want to convert to CSV those events who's 2nd data value under element <EVENT> is 4627. It would only convert those events and in the case of the input below, both mentioned below. <XML><HEADER>1.0,770162,20121009133435,3,</HEADER>20121009133435,721,5,1,0,0,0,00:00,00:00,<EVENT>00032134826064957,4627,</EVENT><DRUG>1,1872161156,7,0,10000</DRUG><DOSE>1,0,5000000,0,10000000,0</DOSE><CAREAREA>1 </CAREAREA><ENCOUNTER></ENCOUNTER><ADVISORY>Keep it simple or spell tham ALL out. For some reason that is not the case please press the on button when trying to activate device codes also available on list</ADVISORY><CAREGIVER></CAREGIVER><PATIENT></PATIENT><LOCATION>20121009133435,00-1d-71-0a-71-80,-66</LOCATION><ROUTE></ROUTE><SITE></SITE><POWER>0,50</POWER></XML> <XML><HEADER>2.0,773162,20121009133435,3,</HEADER>20121004133435,761,5,1,0,0,0,00:00,00:00,<EVENT>00032134826064957,4627,</EVENT><DRUG>1,18735166156,7,0,10000</DRUG><DOSE>1,0,5000000,0,10000000,0</DOSE><CAREAREA>1 </CAREAREA><ENCOUNTER></ENCOUNTER><ADVISORY>Keep it simple or spell tham ALL out. For some reason that is not the case please press the on button when trying to activate device codes also available on list</ADVISORY><CAREGIVER></CAREGIVER><PATIENT></PATIENT><LOCATION>20121009133435,00-1d-71-0a-71-80,-66</LOCATION><ROUTE></ROUTE><SITE></SITE><POWER>0,50</POWER></XML> .. goes on What my approach has been so far is to convert everything to CSV and store it in some sort of data structure and then query that data structure line by line and look if that number exists and if yes, write it to a file line by line. My function takes the path of the XML file and the number we are looking for in the XML data as parameters. I'm new to C# and I cannot understand how I would go about changing my function above. Any help will be appreciated! EDIT: Sample Input: <XML><HEADER>1.0,770162,20121009133435,3,</HEADER>20121009133435,721,5,1,0,0,0,00:00,00:00,<EVENT>00032134826064957,4627,</EVENT><DRUG>1,1872161156,7,0,10000</DRUG><DOSE>1,0,5000000,0,10000000,0</DOSE><CAREAREA>1 </CAREAREA><ENCOUNTER></ENCOUNTER><ADVISORY>Keep it simple or spell tham ALL out. For some reason that is not the case please press the on button when trying to activate device codes also available on list</ADVISORY><CAREGIVER></CAREGIVER><PATIENT></PATIENT><LOCATION>20121009133435,00-1d-71-0a- <XML><HEADER>1.0,770162,20121009133435,3,</HEADER>20121009133435,721,5,1,0,0,0,00:00,00:00,<EVENT>00032134826064957,4623,</EVENT><DRUG>1,1872161156,7,0,10000</DRUG><DOSE>1,0,5000000,0,10000000,0</DOSE><CAREAREA>1 </CAREAREA><ENCOUNTER></ENCOUNTER><ADVISORY>Keep it simple or spell tham ALL out. For some reason that is not the case please press the on button when trying to activate device codes also available on list</ADVISORY><CAREGIVER></CAREGIVER><PATIENT></PATIENT><LOCATION>20121009133435,00-1d-71-0a- Required Output: 1.0,770162,20121009133435,3,,20121009133435,721,5,1,0,0,0,00:00,00:00,,00032134 26064957,4627,1,,1872161156,7,0,10000,1,0,5000000,0,10000000,0,1 ,,Keep it simple or spell tham ALL out. For some reason that is not the case please press the on button when trying to activate device codes also available on list,,,20121009133435,00-1d-71-0a-71-80,-66,,,0,50 The above will be the case if I call xmlToCSVfiltered(file, 4627); Also note that, the output will be a single horizontal line as in CSV files but I can't really format it here for it to look like that.
I changed XmlDocumnet to XDocument so I can use Xml Linq. I also for testing used a StringReader to read the string instead of reading from a file. You can convert code back to your original File.ReadAlltext. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml; using System.Xml.Linq; using System.IO; using System.Text.RegularExpressions; namespace ConsoleApplication1 { class Program { const string FILENAME2 = #"c:\temp\test.txt"; static void Main(string[] args) { string input = "<XML><HEADER>1.0,770162,20121009133435,3,</HEADER>20121009133435,721,5,1,0,0,0,00:00,00:00,<EVENT>00032134826064957,4627,</EVENT><DRUG>1,1872161156,7,0,10000</DRUG><DOSE>1,0,5000000,0,10000000,0</DOSE><CAREAREA>1 </CAREAREA><ENCOUNTER></ENCOUNTER><ADVISORY>Keep it simple or spell\n" + "tham ALL out. For some reason \n" + "that is not the case\n" + "please press the on button\n" + "when trying to activate\n" + "device codes also available on\n" + "list</ADVISORY><CAREGIVER></CAREGIVER><PATIENT></PATIENT><LOCATION>20121009133435,00-1d-71-0a-71-80,-66</LOCATION><ROUTE></ROUTE><SITE></SITE><POWER>0,50</POWER></XML>\n" + "<XML><HEADER>2.0,773162,20121009133435,3,</HEADER>20121004133435,761,5,1,0,0,0,00:00,00:00,<EVENT>00032134826064957,4627,</EVENT><DRUG>1,18735166156,7,0,10000</DRUG><DOSE>1,0,5000000,0,10000000,0</DOSE><CAREAREA>1 </CAREAREA><ENCOUNTER></ENCOUNTER><ADVISORY>Keep it simple or spell\n" + "tham ALL out. For some reason\n" + "that is not the case\n" + "please press the on button\n" + "when trying to activate\n" + "device codes also available on\n" + "list</ADVISORY><CAREGIVER></CAREGIVER><PATIENT></PATIENT><LOCATION>20121009133435,00-1d-71-0a-71-80,-66</LOCATION><ROUTE></ROUTE><SITE></SITE><POWER>0,50</POWER></XML>\n"; xmlToCSVfiltered(input, 4627); } static public void xmlToCSVfiltered(string p, int e) { //string all_lines1 = File.ReadAllText(p); StringReader reader = new StringReader(p); string all_lines1 = reader.ReadToEnd(); all_lines1 = "<Root>" + all_lines1 + "</Root>"; XDocument doc_all = XDocument.Parse(all_lines1); StreamWriter write_all = new StreamWriter(FILENAME2); List<XElement> rows_all = doc_all.Descendants("XML").Where(x => x.Element("EVENT").Value.Split(new char[] {','}).Skip(1).Take(1).FirstOrDefault() == e.ToString()).ToList(); List<string[]> filtered = new List<string[]>(); foreach (XElement rowtemp in rows_all) { List<string> children_all = new List<string>(); foreach (XElement childtemp in rowtemp.Elements()) { children_all.Add(Regex.Replace(childtemp.Value, "\\s+", " ")); // <------- Fixed the Bug , Advisories dont span } string.Join(",", children_all.ToArray()); //write_all.WriteLine(string.Join(",", children_all.ToArray())); if (children_all.Contains(e.ToString())) { filtered.Add(children_all.ToArray()); write_all.WriteLine(children_all); } } write_all.Flush(); write_all.Close(); foreach (var res in filtered) { Console.WriteLine(string.Join(",", res)); } } } }
I have made some assumptions since it was not clear to me from the question Assumptions 1. I am assuming you know that you need to check node event and you need to second position element from there. 2. You know the delimiter between the values in node. for eg. ',' here in events public void xmlToCSVfiltered(string p, int e, string nodeName, char delimiter) { //get the xml node XDocument xml = XDocument.Load(p); //get the required node. I am assuming you would know. For eg. Event Node var requiredNode = xml.Descendants(nodeName); foreach (var node in requiredNode) { if (node == null) continue; //Also here, I am assuming you have the delimiter knowledge. var valueSplit = node.Value.Split(delimiter); foreach (var value in valueSplit) { if (value == e.ToString()) { AddToCSV(); } } } }
Parsing an UN XML file in C#
I'm trying to parse an XML file from UN website (http://www.un.org/sc/committees/1267/AQList.xml) using c#. There is one problem I'm constantly having with this file, and that's the number of child tags varies from one <.INDIVIDUAL.> tag to another. One example is <.FORTH_NAME.> child tag. I've tried a number of different approaches, but somehow I always seem to be stuck with the same problem, and that's different number of child tags inside <.INDIVIDUAL.> tag. What I'm trying to achieve is to collect all the tags and their values under one <.INDIVIDUAL.> tag, and then insert only those I want into my database. If a tag is missing, for example <.FOURTH_NAME.>, than I need to insert only first three names into the database, and skip the fourth. I've tried using Linq to XML, and here are some examples: XDocument xdoc = XDocument.Load(path); var tags = (from t in xdoc.Descendants("INDIVIDUALS") from a in t.Elements("INDIVIDUAL") select new { Tag = a.Name, val = a.Value }); foreach (var obj in tags) { Console.WriteLine(obj.Tag + " - " + obj.val + "\t"); //insert SQL goes here } or: but this one only collects non empty FOURTH_NAME tags... var q = (from c in xdoc.Descendants("INDIVIDUAL") from _1 in c.Elements("FIRST_NAME") from _2 in c.Elements("SECOND_NAME") from _3 in c.Elements("THIRD_NAME") from _4 in c.Elements("FOURTH_NAME") where _1 != null && _2 != null && _3 != null && _4 != null select new { _1 = c.Element("FIRST_NAME").Value, _2 = c.Element("SECOND_NAME").Value, _3 = c.Element("THIRD_NAME").Value, _4 = c.Element("FOURTH_NAME").Value }); foreach (var obj in q) { Console.WriteLine("Person: " + obj._1 + " - " + obj._2 + " - " + obj._3 + " - " + obj._4); //insert SQL goes here } Any ideas??
Instead of calling Value on the element, consider using a string cast. LINQ to XML safely returns null if the element doesn't exist. Try the following: var data = XElement.Load(#"http://www.un.org/sc/committees/1267/AQList.xml"); var individuals = data.Descendants("INDIVIDUAL") .Select(i => new { First = (string)i.Element("FIRST_NAME"), Middle = (string)i.Element("SECOND_NAME"), Last = (string)i.Element("THIRD_NAME") }); If you want to be more flexible and get all of the name fields, you can do something like the following. (I'll leave the process of grouping individuals as an additional homework assignment ;-) data.Descendants("INDIVIDUAL").Elements() .Where (i =>i.Name.LocalName.EndsWith("_NAME" )) .Select(i => new { FieldName= i.Name.LocalName, Value=i.Value});
Why don't you use XmlSerializer and LINQ instead ? As explained in this answer, generate your classes by pasting in a new CS file : menu EDIT > Paste Special > Paste XML As Classes. Then grab your data as easily as follows : var serializer = new XmlSerializer(typeof (CONSOLIDATED_LIST)); using (FileStream fileStream = File.OpenRead(#"..\..\aqlist.xml")) { var list = serializer.Deserialize(fileStream) as CONSOLIDATED_LIST; if (list != null) { var enumerable = list.INDIVIDUALS.Select(s => new { FirstName = s.FIRST_NAME, SecondName = s.SECOND_NAME, ThirdName = s.THIRD_NAME, FourthName = s.FOURTH_NAME }); } } You can then specify any predicate that better suits your needs. Going this path will be a huge time-saver and less error-prone, no need to use strings to access fields, strong typing etc ...
Archiving each string using by looping through an array
I am currently making a piece of software that will allow the user to enter up to 6 directories, each directory is saved as a string (within an array) the loop is then meant to check through the array and any that are not null i.e. actually have a directory assigned are meant to be zipped into their own archive. This is the code I have so far. private void ZipIt() { int nxtFileNum = 0; string Destination = #"C:\tmpZip" + nxtFileNum + ".zip"; // Check all fields, check if empty, if not save to Selection array // Seems a inefficient - Possibly loop through Text box control type and collect? if (String.IsNullOrEmpty(tboxSelect1.Text) == false) { BckupArray[0] = tboxSelect1.Text; }; if (String.IsNullOrEmpty(tboxSelect2.Text) == false) { BckupArray[1] = tboxSelect2.Text; }; if (String.IsNullOrEmpty(tboxSelect3.Text) == false) { BckupArray[2] = tboxSelect3.Text; }; if (String.IsNullOrEmpty(tboxSelect4.Text) == false) { BckupArray[3] = tboxSelect4.Text; }; if (String.IsNullOrEmpty(tboxSelect5.Text) == false) { BckupArray[4] = tboxSelect5.Text; }; if (String.IsNullOrEmpty(tboxSelect6.Text) == false) { BckupArray[5] = tboxSelect6.Text; }; // Create a new ZipFile entity and then loop through each array member, checking if // it has an assigned value, if so compress it, if not, skip it. using (ZipFile ZipIt = new ZipFile()) { nxtFileNum++; foreach (String q in BckupArray) { if (q != null) { ZipIt.AddDirectory(q); ZipIt.Comment = "This archive was created at " + System.DateTime.Now.ToString("G"); ZipIt.Save(Destination); } } } } What I am trying to get this to do is save the first user given location to tmpZip0.7z, the second to tmpZip1.7z and so on however at the moment all it is doing is adding each directory to tmpZip0.zip. Also as a side note, how would I get it to name each archive after the directory selected to be archived? I am currently using DotNetZip (Ionic.Zip) dll. I hope I gave enough information guys.
You need to switch some stuff: foreach (String q in BckupArray) { nxtFileNum++; if (q != null) { using (ZipFile ZipIt = new ZipFile()) { string Destination = #"C:\tmpZip" + nxtFileNum + ".zip"; ZipIt.AddDirectory(q); ZipIt.Comment = "This archive was created at " + System.DateTime.Now.ToString("G"); ZipIt.Save(Destination); } } } Reasons: The string Destination is fixed after you created it. It doesn't change, just because you increment nxtFileNum. You created only one ZipFile and you incremented nxtFileNum only once, because the those were outside of your foreach loop Putting the part that creates the ZipFile into the if makes sure an instance is only created if it is really used.
Well, you can do this with: var strings = Controls.OfType<TextBox>() .Select(x => x.Text) .Where(text => !string.IsNullOrEmpty(text)) .ToList(); using (ZipFile ZipIt = new ZipFile()) { nxtFileNum++; string comment = string.Format("This archive was created at {0:G}", DateTime.Now); foreach (string directory in strings) { ZipIt.AddDirectory(directory); ZipIt.Comment = comment; ZipIt.Save(Destination + "." + nxtFileNum); } } That will obviously pull all the textboxes though. An alternative is to have a collection of type List<TextBox> or something similar instead of the six different variables. Note that that will always create .1, .2, .3 etc even if the user didn't specify the first three names. Let me know if you want to be absolutely faithful to the positioning the user gave. It's not clear to me that you should really be reusing the same ZipFile object, by the way. I'd expect this to be more appropriate: string comment = string.Format("This archive was created at {0:G}", DateTime.Now); int fileIndex = 0; foreach (string directory in strings) { fileIndex++; using (ZipFile zipFile = new ZipFile()) { zipFile.AddDirectory(directory); zipFile.Comment = comment; zipFile.Save(Destination + "." + fileIndex); } } (Note how I've renamed the variables to be more conventional, by the way - variables typically start with a lower case letter.)