Read elements from XML and use them in a method - c#

i have the following code which checks if some folders were created; The list of folders with full path is stored in an xml file.
namespace InstallationCheck
{
public class Checking
{
public bool result()
{
bool returns = true;
//Reads from xml file the element content from a tag line (ex: esecpath)
string esecpath = Checking.CitXml("C:\\testconfig.xml", "esecpath");
string agentpath = Checking.CitXml("C:\\testconfig.xml", "agentpath");
string datapath = Checking.CitXml("C:\\testconfig.xml", "datapath");
string debugpath = Checking.CitXml("C:\\testconfig.xml", "debugpath");
string helppath = Checking.CitXml("C:\\testconfig.xml", "helppath");
string patchpath = Checking.CitXml("C:\\testconfig.xml", "patchpath");
// Compare the paths from XML with the paths of the app.
List<bool> listtest = new List<bool>();
listtest.Add((Directory.Exists(esecpath) == true));
listtest.Add((Directory.Exists(agentpath) == true));
listtest.Add((Directory.Exists(datapath) == true));
listtest.Add((Directory.Exists(debugpath) == true));
listtest.Add((Directory.Exists(patchpath) == true));
listtest.Add((Directory.Exists(helppath) == true));
//Cheking if any of paths are false
foreach (bool varia in listtest)
{
returns = returns && varia;
}
return returns;
}
// Reading from XML method
public static string CitXml(string xmlpath, string field)
{
XmlTextReader xmlReader = new XmlTextReader(xmlpath);
xmlReader.WhitespaceHandling = WhitespaceHandling.None;
xmlReader.ReadToDescendant(field);
return xmlReader.ReadElementString(field);
}
}
}
Now i need to check if some files were created (many of them), to spare me of manualy adding all in to the code, i wonder how should i do it; i want the code to read the xml file and check if all of the files (from the xml) were created. So i wonder if someone of you can give an ideea, a hint (so i can go read about it), maybe a code example. Thank you.

Well, to see if any were created, re-write the code to be something like:
public bool result()
{
Dictionary<string, string> files = new Dictionary<string, string>();
files.Add("esecpath", "C:\\testconfig.xml");
// ... etc for each file
// if you want to see if any don't exist, then use ...
// if(files.Any(f => !File.Exists(f.Value)))
// else, these are all the created files
var createdFiles = files.Where(f => !File.Exists(f.Value));
if(createdFiles.Count() > 0)
{
// A file doesn't exist! Therefore you are creating it!
}
var directories = files.Select(f => Checking.CitXml(f.Value, f.Key));
return directories.All(d => Directory.Exists(d));
}

Related

List to Dictionary and back again

I have read many posts about dictionaries and serialization etc. and they all have different ways of doing things.
Being new to coding (to an extent) I am having trouble figuring out how to get my dictionary to a list or serialized to get it saved then reloaded on play. This is Unity BTW.
So here is what I am using.
I have an application manager that is mainly just a set of BOOL values that tells my mission system if something has been completed or not. Something like "GetWeapon FALSE" (then when they pick it up it changes to TRUE which tells the mission system to move to the next objective or complete)
So I have a starting list of keys,values...these then get placed into a dictionary and the values are changed within that.
I need to be able to save those values and reload them on LOAD (default PLAY mode is newgame--as you see below it resets the dictionary and copies in the starting states). I know it can't be as difficult as I am making it, just not understanding the whole serialize thing.
Most sites are showing a dictionary like {key,value} so I am getting lost on iterating through the dictionary and capturing the pairs and saving them.
Here is partial code for the appmanager (it is a singleton):
// This holds any states you wish set at game startup
[SerializeField] private List<GameState> _startingGameStates = new List<GameState>();
// Used to store the key/values pairs in the above list in a more efficient dictionary
private Dictionary<string, string> _gameStateDictionary = new Dictionary<string, string>();
void Awake()
{
// This object must live for the entire application
DontDestroyOnLoad(gameObject);
ResetGameStates();
}
void ResetGameStates()
{
_gameStateDictionary.Clear();
// Copy starting game states into game state dictionary
for (int i = 0; i < _startingGameStates.Count; i++)
{
GameState gs = _startingGameStates[i];
_gameStateDictionary[gs.Key] = gs.Value;
}
OnStateChanged.Invoke();
}
public GameState GetStartingGameState(string key)
{
for (int i = 0; i < _startingGameStates.Count; i++)
{
if (_startingGameStates[i] != null && _startingGameStates[i].Key.Equals(key))
return _startingGameStates[i];
}
return null;
}
// Name : SetGameState
// Desc : Sets a Game State
public bool SetGameState(string key, string value)
{
if (key == null || value == null) return false;
_gameStateDictionary[key] = value;
OnStateChanged.Invoke();
return true;
}
Tried something similar to this:
Dictionary<string, string> _gameStateDictionary = new Dictionary<string, string>
{
for (int i = 0; i < _gameStateDictionary.Count; i++)
string json = JsonConvert.SerializeObject(_gameStateDictionary, Formatting.Indented);
Debug.Log(json);
{
}
However all I got was the first item in the list. (I did modify the above in a for loop) I know the above is wrong, I did other iterations to just to get the dictionary to print out in the console.
Anyway, just asking for a little code help to save and load a dictionary.
Since you want to save and load those persistent anyway, personally I would not use the Inspector and bother with Unity serialization at all but rather directly serialize/deserialize from and to json.
Simply place your default dictionary as a simple json file like e.g.
{
"Key1" : "Value1",
"Key2" : "Value2",
"Key3" : "Value3",
...
}
within the folder Assets/StreamingAssets (create it if it doesn't exists). This will be your default fallback file if none exists so far.
On runtime you will load it from Application.streamingAssetsPath.
Then when saving you will not put it there but rather to the Application.persistentDataPath
using Newtonsoft.Json;
...
[SerializeField] private string fileName = "example.json";
private string defaultStatesPath => Path.Combine(Application.streamingAssetsPath, fileName);
private string persistentStatesPath => Path.Combine(Application.persistentDataPath, fileName);
private Dictionary<string, string> _gameStateDictionary
public void Load()
{
var filePath = persistentStatesPath;
if(!File.Exists(filePath))
{
filePath = defaultStatesPath;
}
var json = File.ReadAllText(filePath);
_gameStateDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
}
public void Save()
{
var filePath = persistentStatesPath;
if(!Directory.Exists(Application.persistentDataPath))
{
Directory.CreateDirectory(Application.persistentDataPath);
}
var json = JsonConvert.SerializeObject(_gameStateDictionary, Formatting.intended);
File.WriteAllText(filePath, json);
}
If you then still want to edit the default via the Inspector you could merge both and instead of using StreamingAssets do
[SerializeField] private GameState[] defaultStates;
public void Load()
{
var filePath = persistentStatesPath;
if(!File.Exists(filePath))
{
LoadDefaults();
return;
}
var json = File.ReadAllText(filePath);
_gameStateDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
}
public void LoadDefaults()
{
_gameStateDictionary = defaultStates.ToDictionary(gameState => gameState.Key, gameState => gameState.Value);
}
And then btw you could do
public string GetState(string key, string fallbackValue = "")
{
if(_gameStateDictionary.TryGetValue(key, out var value))
{
return value;
}
return fallbackValue;
}
You can iterate the dictionary by using its Keys collection.
foreach (var key in myDictionary.Keys)
{
var val = myDictionary[key];
}
Populate your data structure with the key/value pairs and serialize the list of items.
var missionItems = new List<MissionItem>();
foreach (var key in myDictionary.Keys)
{
var missionItem = new MissionItem();
missionItem.Key = key;
missionItem.Value = myDictionary[key];
missionItems.Add(missionItem);
}
var json = JsonUtility.ToJson(missionItems);
Loading is straight forward, deserialize your data, then loop through all the items and add each one to the empty dicitionary.
var missionItems = JsonUtility.FromJson<List<MissionItem>>(json);
foreach (var item in missionItems)
{
myDictionary.Add(item.Key, item.Value);
}
Here is my final setup.
I call these on button press in another script (the UI pause menu save/load buttons)
private bool useEncryption = true;
private string encryptionCodeWord = "CodeWordHere";
private static string fileName = "name.json";
private static string defaultStatesPath => Path.Combine(Application.streamingAssetsPath, fileName);
private static string persistentStatesPath => Path.Combine(Application.persistentDataPath, fileName);
public void SaveStates(int slotIndex)
{
var filePath = persistentStatesPath;
if(!Directory.Exists(Application.persistentDataPath))
{
Directory.CreateDirectory(Application.persistentDataPath);
}
var json = JsonConvert.SerializeObject(_gameStateDictionary, Formatting.Indented);
if (useEncryption)
{
Debug.Log("Using Encryption");
json = EncryptDecrypt(json);
}
File.WriteAllText(filePath + slotIndex, json);
}
public void LoadStates(int SlotIndex)
{
var filePath = persistentStatesPath;
if(!File.Exists(filePath + SlotIndex))
{
filePath = defaultStatesPath;
}
var json = File.ReadAllText(filePath + SlotIndex);
if (useEncryption)
{
Debug.Log("Using Encryption");
json = EncryptDecrypt(json);
}
_gameStateDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
}
private string EncryptDecrypt(string data)
{
string modifiedData = "";
for (int i = 0; i < data.Length; i++)
{
modifiedData += (char)(data[i] ^ encryptionCodeWord[i % encryptionCodeWord.Length]);
}
return modifiedData;
}

twice reading xml file with linq

I am trying to read xml file. and then extract some useful data to draw graphs.. I have achieved the desired output.. But the problem is my program is twice reading the xml file to extract the useful data.. This takes some extra time. Is there some other way to read the file once only. ? Thanks
<?xml version="1.0" encoding="UTF-8"?>
<CanConformanceTesterLog Version="4.1">
<TestProperties>
<Item name="IUT Name" value="Reference"/>
<Item name="PG Clock Period" value="1000 ns"/>
</TestProperties>
<SignalData SamplingPeriod="1000.000 ns" DataWidth="16 bit">
<Signal>
<Id>IUT_RX</Id>
<InitState>1</InitState>
<![CDATA[HQFPAVkBiwGVAZ8BqQHHAdEBAwINAjUCPwJxAnsCrQK3AsEC1QLzAv0CEQMbAzkDTQNrA3UDfwOJA7sDxQPtA/cDKQQzBEcEUQSDBI0EtQTJBN0E5wTxBAUFDwUZBS0FNwVBBUsFVQWHFZEVmxWlFa8VuRXDFc0V1xXhFesV9RX/FTEWOxZFFk8WgRaLFpUWnxapFscW0RbbFuUW7xYDFyEXPxdJF1MXGhgkGC4YTBhWGHQYfhiwGLoY2BjiGBQZHhkoGTIZUBlaGXgZghmgGaoZvhnbGeUZ9RwTHR0dTx1ZHYsdlR29Hccd+R0DHg0eFx5JHlMeZx6ZHsEe6R4lH5Qfsh+8H+4f+B8qIDQgXCBmIJggoiCsILYg6CDyIAYhOCFgIYghxCEzIlEiWyKNIpciySLTIvsiBSM3I0EjSyNVI4cjmyOlI9cj/yMTJB0kpyaxJrsmxSbPJgEnCyc9J0cnZSdvJ6EnqyfdJ+cnGSgjKC0oQShLKF8ocyiHKJsopSivKLkowyjWKOAo8jQkNS41YDVqNZw1pjXENc41ADYKNhQ2HjZGNlA2WjZkNm42eDaWNqo2tDbHNtE2uDd=]]>
</Signal>
<Signal>
<Id>IUT_TX</Id>
<InitState>1</InitState>
<![CDATA[SwVVBYcVkRWbFaUVrxW5FcMVzRXXFeEV6xX1Ff8VMRY7FkUWTxaBFosWlRafFqkWxxbRFtsW5RbvFgMXIRc/FxoYJBguGEwYVhh0GH4YsBi6GNgY4hgUGR4ZKBkyGVAZWhl4GYIZoBmqGb4Z6B4kH4ghxCETJB0kpyaxJrsmxSbPJgEnCyc9J0cnZSdvJ6EnqyfdJ+cnGSgjKC0oQShLKF8ocyiHKJsopSivKLkowyjyNCQ1LjVgNWo1nDWmNcQ1zjUANgo2FDYeNkY2UDZaNmQ2bjZ4NpY2qja0Nrg3]]>
</Signal>
</SignalData>
</CanConformanceTesterLog>
I have function that reads the data of tag "SignalData".then after reading this data it calls another function and pass the name of xml file,dataWidth,samplingPeriod.
The second function then reads "Signal" tag.. and then extract the data from every "Signal". Finally when everything is done then a function is called to draw the graphs...
private bool SignalDataInfo(string fileName)
{
var xdoc = XDocument.Load(fileName);
if (xdoc != null)
{
var signalData = xdoc.Descendants("SignalData");
foreach (var signal in signalData)
{
var width = signal.Attribute("DataWidth").Value;
string dataWidth = width.Substring(0, width.IndexOf(" "));
var period = signal.Attribute("SamplingPeriod").Value;
string samplingPeriod = period.Substring(0, period.IndexOf(" "));
SignalData(fileName,dataWidth, samplingPeriod);
}
return true;
}
else
return false;
}
public bool SignalData(string fileName,string width, string period)
{var xdoc = XDocument.Load(fileName);
if (xdoc != null)
{
var signalData = xdoc.Descendants("Signal");
foreach (var signal in signalData)
{ // extract data from every signal }
return true;
else false;
}
Here I create a sample console app for you to extract data from both of your SignalData and Signal.
I think you would be in search of code like below.
In below code snippet you would use result in your program where you want to read data inside xml.
So by this way you don't need to write two different methods and load your xml each time when your methods called.
class Program
{
static void Main(string[] args)
{
XDocument doc = XDocument.Load(#"Your xml document path");
var result = (from o in doc.Descendants("SignalData")
from i in o.Descendants("Signal")
select new
{
dataWidth = o.Attribute("DataWidth").Value.Substring(0, o.Attribute("DataWidth").Value.IndexOf(" ")),
period = o.Attribute("SamplingPeriod").Value.Substring(0, o.Attribute("SamplingPeriod").Value.IndexOf(" ")),
Id = i.Elements("Id").Select(item => (string)item).FirstOrDefault(),
InitState = i.Elements("InitState").Select(item => (string)item).FirstOrDefault(),
cdata = i.Value
}).ToList();
foreach (var item in result)
{
Console.WriteLine($"dataWidth: { item.dataWidth}, \t period: {item.period}, \t Id: {item.Id}, \t InitState: {item.InitState}");
}
Console.ReadLine();
}
}
Output: cdata excluded from output.
You can just load the XML file once by saving the XDocument that was loaded in a class variable (say private XDocument xDoc;) instead of creating an instance on each method. Also, it would help if you just retrieve the XML data separately, in this case, the loadData() method which you can use to initialize the data once. This will also give your code, somehow, separation of concerns. See code below:
private XDocument xDoc;
private void loadData(string fileName)
{
xDoc = XDocument.Load(fileName);
}
private bool SignalDataInfo()
{
if (xDoc != null)
{
var signalData = xDoc.Descendants("SignalData");
foreach (var signal in signalData)
{
var width = signal.Attribute("DataWidth").Value;
string dataWidth = width.Substring(0, width.IndexOf(" "));
var period = signal.Attribute("SamplingPeriod").Value;
string samplingPeriod = period.Substring(0, period.IndexOf(" "));
SignalData(fileName,dataWidth, samplingPeriod);
}
return true;
}
else
return false;
}
public bool SignalData(string width, string period)
{
if (xDoc != null)
{
var signalData = xDoc.Descendants("Signal");
foreach (var signal in signalData)
{ // extract data from every signal }
return true;
else false;
}
Hope this helps!

How to create a CSV file from a XML file

I am very new at C#. In my project I need to create a csv file which will get data from a xml data. Now, I can get data from XML, and print in looger for some particulaer attributes from xml. But I am not sure how can I store my Data into CSV file for that particular attribues.
Here is my XML file that I need to create a CSV file.
<?xml version="1.0" encoding="utf-8"?>
<tlp:WorkUnits xmlns:tlp="http://www.timelog.com/XML/Schema/tlp/v4_4"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.timelog.com/XML/Schema/tlp/v4_4 http://www.timelog.com/api/xsd/WorkUnitsRaw.xsd">
<tlp:WorkUnit ID="130">
<tlp:EmployeeID>3</tlp:EmployeeID>
<tlp:AllocationID>114</tlp:AllocationID>
<tlp:TaskID>239</tlp:TaskID>
<tlp:ProjectID>26</tlp:ProjectID>
<tlp:ProjectName>LIK Template</tlp:ProjectName>
<tlp:CustomerId>343</tlp:CustomerId>
<tlp:CustomerName>Lekt Corp Inc.</tlp:CustomerName>
<tlp:IsBillable>1</tlp:IsBillable>
<tlp:ApprovedStatus>0</tlp:ApprovedStatus>
<tlp:LastModifiedBy>AL</tlp:LastModifiedBy>
</tlp:WorkUnit>
And my Code where I am getting this value in logger.But I am not sure how can I create a csv file that stores that value in order.
Edited
namespace TimeLog.ApiConsoleApp
{
/// <summary>
/// Template class for consuming the reporting API
/// </summary>
public class ConsumeReportingApi
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(ConsumeReportingApi));
public static void Consume()
{
if (ServiceHandler.Instance.TryAuthenticate())
{
if (Logger.IsInfoEnabled)
{
Logger.Info("Successfully authenticated on reporting API");
}
var customersRaw = ServiceHandler.Instance.Client.GetWorkUnitsRaw(ServiceHandler.Instance.SiteCode,
ServiceHandler.Instance.ApiId,
ServiceHandler.Instance.ApiPassword,
WorkUnit.All,
Employee.All,
Allocation.All,
Task.All,
Project.All,
Department.All,
DateTime.Now.AddDays(-5).ToString(),
DateTime.Now.ToString()
);
if (customersRaw.OwnerDocument != null)
{
var namespaceManager = new XmlNamespaceManager(customersRaw.OwnerDocument.NameTable);
namespaceManager.AddNamespace("tlp", "http://www.timelog.com/XML/Schema/tlp/v4_4");
var workUnit = customersRaw.SelectNodes("tlp:WorkUnit", namespaceManager);
var output = new StringBuilder();
output.AppendLine("AllocationID,ApprovedStatus,CustomerId,CustomerName,EmployeeID");
if (workUnit != null)
{
foreach (XmlNode customer in workUnit)
{
var unit = new WorkUnit();
var childNodes = customer.SelectNodes("./*");
if (childNodes != null)
{
foreach (XmlNode childNode in childNodes)
{
if (childNode.Name == "tlp:EmployeeID")
{
unit.EmployeeID = Int32.Parse(childNode.InnerText);
}
if (childNode.Name == "tlp:EmployeeFirstName")
{
unit.EmployeeFirstName = childNode.InnerText;
}
if (childNode.Name == "tlp:EmployeeLastName")
{
unit.EmployeeLastName = childNode.InnerText;
}
if (childNode.Name == "tlp:AllocationID")
{
unit.AllocationID = Int32.Parse(childNode.InnerText);
}
if (childNode.Name == "tlp:TaskName")
{
unit.TaskName = childNode.InnerText;
}
}
}
output.AppendLine($"{unit.EmployeeID},{unit.EmployeeFirstName},{unit.EmployeeLastName},{unit.AllocationID},{unit.TaskName}");
//Console.WriteLine("---");
}
Console.WriteLine(output.ToString());
File.WriteAllText("c:\\...\\WorkUnits.csv", output.ToString());
}
}
else
{
if (Logger.IsWarnEnabled)
{
Logger.Warn("Failed to authenticate to reporting API");
}
}
}
}
}
}
You want to write the columns in the correct order to the CSV (of course), so you need to process them in the correct order. Two options:
intermediate class
Create a new class (let's call it WorkUnit) with properties for each of the columns that you want to write to the CSV. Create a new instance for every <tlp:WorkUnit> node in your XML and fill the properties when you encounter the correct subnodes. When you have processed the entire WorkUnit node, write out the properties in the correct order.
var output = new StringBuilder();
foreach (XmlNode customer in workUnit)
{
// fresh instance of the class that holds all columns (so all properties are cleared)
var unit = new WorkUnit();
var childNodes = customer.SelectNodes("./*");
if (childNodes != null)
{
foreach (XmlNode childNode in childNodes)
{
if(childNode.Name== "tlp:EmployeeID")
{
// employeeID node found, now write to the corresponding property:
unit.EmployeeId = childNode.InnerText;
}
// etc for the other XML nodes you are interested in
}
// all nodes have been processed for this one WorkUnit node
// so write a line to the CSV
output.AppendLine($"{unit.EmployeeId},{unit.AllocationId}, etc");
}
read in correct order
Instead of using foreach to loop through all subnodes in whatever order they appear, search for specific subnodes in the order you want. Then you can write out the CSV in the same order. Note that even when you don't find some subnode, you still need to write out the separator.
var output = new StringBuilder();
foreach (XmlNode customer in workUnit)
{
// search for value for first column (EmployeeID)
var node = workUnit.SelectSingleNode("tlp:EmployeeID");
if (node != null)
{
output.Append(node.InnerText).Append(',');
}
else
{
output.Append(','); // no content, but we still need a separator
}
// etc for the other columns
And of course watch out for string values that contain the separator.
Assuming that you put your XML data into List
StringBuilder str = new StringBuilder();
foreach (var fin list.ToList())
{
str.Append(fin.listfield.ToString() + ",");
}
to create a new line:
str.Replace(",", Environment.NewLine, str.Length - 1, 1);
to save:
string filename=(DirectoryPat/filename.csv");
File.WriteAllText(Filename, str.ToString());
Try this:
var output = new StringBuilder();
output.AppendLine("AllocationID,ApprovedStatus,CustomerId,CustomerName,EmployeeID");
if (workUnit != null)
{
foreach (XmlNode customer in workUnit)
{
var unit = new WorkUnit();
var childNodes = customer.SelectNodes("./*");
if (childNodes != null)
{
for (int i = 0; i<childNodes.Count; ++i)
{
XmlNode childNode = childNodes[i];
if (childNode.Name == "tlp:EmployeeID")
{
unit.EmployeeID = Int32.Parse(childNode.InnerText);
}
if (childNode.Name == "tlp:EmployeeFirstName")
{
unit.EmployeeFirstName = childNode.InnerText;
}
if (childNode.Name == "tlp:EmployeeLastName")
{
unit.EmployeeLastName = childNode.InnerText;
}
if (childNode.Name == "tlp:AllocationID")
{
unit.AllocationID = Int32.Parse(childNode.InnerText);
}
if (childNode.Name == "tlp:TaskName")
{
unit.TaskName = childNode.InnerText;
}
output.Append(childNode.InnerText);
if (i<childNodes.Count - 1)
output.Append(",");
}
output.Append(Environment.NewLine);
}
}
Console.WriteLine(output.ToString());
File.WriteAllText("c:\\Users\\mnowshin\\projects\\WorkUnits.csv", output.ToString());
}
You can use this sequence:
a. Deserialize (i.e. convert from XML to C# objects) your XML.
b. Write a simple loop to write the data to a file.
The advantages of this sequence:
You can use a list of your data/objects "readable" that you can add any other access code to it.
If you XML schema changed at any time, you can maintain the code very easily.
The solution
a. Desrialize:
Copy you XML file contents. Note You should modify your XML input before coping it.. You should double the WorkUnit node, in order to tell Visual Studio that you would have a list of this node nested inside WorkUnits node.
From Visual Studio Menus select Edit -> Paste Special -> Paste XML as Classes.
Use the deserialize code.
var workUnitsNode = customersRaw.SelectSingleNode("tlp:WorkUnits", namespaceManager);
XmlSerializer ser = new XmlSerializer(typeof(WorkUnits));
WorkUnits workUnits = (WorkUnits)ser.Deserialize(workUnitsNode);
b. Write the csv file
StringBuilder csvContent = new StringBuilder();
// add the header line
csvContent.AppendLine("AllocationID,ApprovedStatus,CustomerId,CustomerName,EmployeeID");
foreach (var unit in workUnits.WorkUnit)
{
csvContent.AppendFormat(
"{0}, {1}, {2}, {3}, {4}",
new object[]
{
unit.AllocationID,
unit.ApprovedStatus,
unit.CustomerId,
unit.CustomerName,
unit.EmployeeID
// you get the idea
});
csvContent.AppendLine();
}
File.WriteAllText(#"G:\Projects\StackOverFlow\WpfApp1\WorkUnits.csv", csvContent.ToString());
You can use Cinchoo ETL - if you have room to use open source library
using (var csvWriter = new ChoCSVWriter("sample1.csv").WithFirstLineHeader())
{
using (var xmlReader = new ChoXmlReader("sample1.xml"))
csvWriter.Write(xmlReader);
}
Output:
ID,tlp_EmployeeID,tlp_AllocationID,tlp_TaskID,tlp_ProjectID,tlp_ProjectName,tlp_CustomerId,tlp_CustomerName,tlp_IsBillable,tlp_ApprovedStatus,tlp_LastModifiedBy
130,3,114,239,26,LIK Template,343,Lekt Corp Inc.,1,0,AL
Disclaimer: I'm the author of this library.

Is it possible in C# to return a array back to the calling program?

Is it possible in C# to return a array back to the calling program? If it is not possible, please say it is not all possible. Another alternative is to create a long string and use string.split(). But that does not look nice.
ExamnationOfReturnsFiled("ABCDE1234E") //Calling program.
public yearsfiled[] ExamnationOfReturnsFiled(string panreceived) //function.
{
int k = 0; //to increment the array element.
string item = panreceived; //string value received call program.
string[] yearsfiled = new string[20];//Declaring a string array.
Regex year = new Regex(#"[0-9]{4}-[0-9]{2}");//to capture 2012-13 like entries.
using (StreamReader Reader1 = new StreamReader(#"C: \Users\Unnikrishnan C\Documents\Combined_Blue_Book.txt"))
{
Regex tofindpan = new Regex(item);//Regular Expression to catch the string from the text file being read.
bool tosearch = false;
Regex blank = new Regex(#"^\s*$"); //to detect a blank line.
while ((str.line1 = Reader1.ReadLine()) != null)
{
Match Tofindpan = tofindpan.Match(#"[A-Z]{5}[0-9]{4}[A-Z]{1}");
Match Blank = blank.Match(line1);
if (Blank.Success)
{
tosearch = false;
}
if (Tofindpan.Success)
{
tosearch = true; //when true the
}
if (tosearch == true)
{
Match Year = year.Match(str.line1);
if (Year.Success)
{
yearsfiled[k] = Year.Value;
k++;
}
}
}
return yearsfiled;
}
}
public string[] ExamnationOfReturnsFiled(string panreceived) //function
you are returning type not variable name change the method signature like above
You should be returning a string[]. Your return type yearsfiled[] is a variable name, not a type name
//from calling programme. Tested and succeeded.
string[] yearsfiled = new string[20];
yearsfiled = ExamnationOfReturnsFiled(item1);
// the function name modified as follows.
public static string[] ExamnationOfReturnsFiled(string panreceived)
{
Everything else as in the original post.
}
//It was tested. And found successful. Thanks so much to #Midhun Mundayadan and #Eavidan.

Retrieving Data From XML File

I seem to be having a problem with retrieving XML values with C#, which I know it is due to my very limited knowledge of C# and .XML.
I was given the following XML file
<PowerBuilderRunTimes>
<PowerBuilderRunTime>
<Version>12</Version>
<Files>
<File>EasySoap110.dll</File>
<File>exPat110.dll</File>
<File>pbacc110.dll</File>
</File>
</PowerBuilderRunTime>
</PowerBuilderRunTimes>
I am to process the XML file and make sure that each of the files in the exist in the folder (that's the easy part). It's the processing of the XML file that I have having a hard time with. Here is what I have done thus far:
var runtimeXml = File.ReadAllText(string.Format("{0}\\{1}", configPath, Resource.PBRuntimes));
var doc = XDocument.Parse(runtimeXml);
var topElement = doc.Element("PowerBuilderRunTimes");
var elements = topElement.Elements("PowerBuilderRunTime");
foreach (XElement section in elements)
{
//pbVersion is grabbed earlier. It is the version of PowerBuilder
if( section.Element("Version").Value.Equals(string.Format("{0}", pbVersion ) ) )
{
var files = section.Elements("Files");
var fileList = new List<string>();
foreach (XElement area in files)
{
fileList.Add(area.Element("File").Value);
}
}
}
My issue is that the String List is only ever populated with one value, "EasySoap110.dll", and everything else is ignored. Can someone please help me, as I am at a loss.
Look at this bit:
var files = section.Elements("Files");
var fileList = new List<string>();
foreach (XElement area in files)
{
fileList.Add(area.Element("File").Value);
}
You're iterating over each Files element, and then finding the first File element within it. There's only one Files element - you need to be iterating over the File elements within that.
However, there are definitely better ways of doing this. For example:
var doc = XDocument.Load(Path.Combine(configPath, Resource.PBRuntimes));
var fileList = (from runtime in doc.Root.Elements("PowerBuilderRunTime")
where (int) runtime.Element("Version") == pbVersion
from file in runtime.Element("Files").Elements("File")
select file.Value)
.ToList();
Note that if there are multiple matching PowerBuilderRunTime elements, that will create a list with all the files of all those elements. That may not be what you want. For example, you might want:
var doc = XDocument.Load(Path.Combine(configPath, Resource.PBRuntimes));
var runtime = doc.Root
.Elements("PowerBuilderRunTime")
.Where(r => (int) r.Element("Version") == pbVersion)
.Single();
var fileList = runtime.Element("Files")
.Elements("File")
.Select(x => x.Value)
.ToList();
That will validate that there's exactly one matching runtime.
The problem is, there's only one element in your XML, with multiple children. You foreach loop only executes once, for the single element, not for its children.
Do something like this:
var fileSet = files.Elements("File");
foreach (var file in fileSet) {
fileList.Add(file.Value);
}
which loops over all children elements.
I always preferred using readers for reading homegrown XML config files. If you're only doing this once it's probably over kill, but readers are faster and cheaper.
public static class PowerBuilderConfigParser
{
public static IList<PowerBuilderConfig> ReadConfigFile(String path)
{
IList<PowerBuilderConfig> configs = new List<PowerBuilderConfig>();
using (FileStream stream = new FileStream(path, FileMode.Open))
{
XmlReader reader = XmlReader.Create(stream);
reader.ReadToDescendant("PowerBuilderRunTime");
do
{
PowerBuilderConfig config = new PowerBuilderConfig();
ReadVersionNumber(config, reader);
ReadFiles(config, reader);
configs.Add(config);
reader.ReadToNextSibling("PowerBuilderRunTime");
} while (reader.ReadToNextSibling("PowerBuilderRunTime"));
}
return configs;
}
private static void ReadVersionNumber(PowerBuilderConfig config, XmlReader reader)
{
reader.ReadToDescendant("Version");
string version = reader.ReadString();
Int32 versionNumber;
if (Int32.TryParse(version, out versionNumber))
{
config.Version = versionNumber;
}
}
private static void ReadFiles(PowerBuilderConfig config, XmlReader reader)
{
reader.ReadToNextSibling("Files");
reader.ReadToDescendant("File");
do
{
string file = reader.ReadString();
if (!string.IsNullOrEmpty(file))
{
config.AddConfigFile(file);
}
} while (reader.ReadToNextSibling("File"));
}
}
public class PowerBuilderConfig
{
private Int32 _version;
private readonly IList<String> _files;
public PowerBuilderConfig()
{
_files = new List<string>();
}
public Int32 Version
{
get { return _version; }
set { _version = value; }
}
public ReadOnlyCollection<String> Files
{
get { return new ReadOnlyCollection<String>(_files); }
}
public void AddConfigFile(String fileName)
{
_files.Add(fileName);
}
}
Another way is to use a XmlSerializer.
[Serializable]
[XmlRoot]
public class PowerBuilderRunTime
{
[XmlElement]
public string Version {get;set;}
[XmlArrayItem("File")]
public string[] Files {get;set;}
public static PowerBuilderRunTime[] Load(string fileName)
{
PowerBuilderRunTime[] runtimes;
using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
var reader = new XmlTextReader(fs);
runtimes = (PowerBuilderRunTime[])new XmlSerializer(typeof(PowerBuilderRunTime[])).Deserialize(reader);
}
return runtimes;
}
}
You can get all the runtimes strongly typed, and use each PowerBuilderRunTime's Files property to loop through all the string file names.
var runtimes = PowerBuilderRunTime.Load(string.Format("{0}\\{1}", configPath, Resource.PBRuntimes));
You should try replacing this stuff with a simple XPath query.
string configPath;
System.Xml.XPath.XPathDocument xpd = new System.Xml.XPath.XPathDocument(cofigPath);
System.Xml.XPath.XPathNavigator xpn = xpd.CreateNavigator();
System.Xml.XPath.XPathExpression exp = xpn.Compile(#"/PowerBuilderRunTimes/PwerBuilderRunTime/Files//File");
System.Xml.XPath.XPathNodeIterator iterator = xpn.Select(exp);
while (iterator.MoveNext())
{
System.Xml.XPath.XPathNavigator nav2 = iterator.Current.Clone();
//access value with nav2.value
}

Categories