Turn array into a POCO object - c#

I'm having trouble converting the following string array into a POCO object.
Given the following:
string files = [
"./Folder/file.ext",
"./Folder/file2.ext",
"./Folder/file3.ext",
"./Folder/nestedfolder/file.ext",
"./Folder2/file1.ext",
"./Folder2/file2.ext",
"./file1.ext",
"./file2.ext",
"./file3.ext",
];
I would like to convert it to something like:
public class HierarchicalSource{
public List<HierarchicalSource> Children = new List <HierarchicalSource> ();
public bool folder { get; set; }
public string FullPath;
public HierarchicalSourceSource(string path) {
this.FullPath = path;
}
}
Where HierarchicalSource is the root, and has a list of children
UPDATE:
I ended up changing the list to a dictionary. There must be a more efficient way to do this, but I did as follows:
string fileList = files.Select(x => x.Remove(0, 2)).ToArray();
var root = new HierarchicalSource("root");
foreach(var f in fileList){
var current = root;
string[] splitFile = f.Split('/');
foreach(var s in splitFile){
if(!current.Children.ContainsKey(s)){
current.Children.Add(s, new List<HierarchicalSource>{ new HierarchicalSource(s) });
}
current = current.Children[s].Last();
}
}
POCO:
public class HierarchicalSource{
public string name;
public Dictionary<string, List<HierarchicalSource>> Children = new Dictionary<string, List<HierarchicalSource>>();
public HierarchicalSource(string name){
this.name = name;
}
}

If I understand you correctly, this requires looping through the array, but it'll allow you to parse each item in the array so you can generate the HierarchicalNode object's values.
var node = new HierarchicalSource();
foreach(var str in files)
{
var pathParts = str.Split('/').ToList();
node.Children.Add(new HierarchicalNode()
{
FullPath = str,
Folder = pathParts[1] // you may need to do some debugging to see what the results for pathParts are instead of just [#]
});
}
Since the FullPath member in HierarchicalNode is public you can set that value without having to go through any constructor.
// using the above code for reference
node.FullPath = whateverThePathYouNeedIs;
Also update that property in the class to use getters and setters
public string FullPath { get; set; }

Related

what is the best way to write a list of items in a text file in tree view form

i have a list of objects , and each object have a list of dependant objects , i want to write the list in a text file in a tree view form .
i tried doing foreach on the list but i can't all dependencies and the correct levels of objects
//the list of all objects
List<Object> objects;
//object Class
class Object {
string name;
List<Object> depandantObj;
}
the expected result must be writen in text file under the form :
object1:
object2
object3:
object5
object1
object6:
object2
etc...
Recursive method to append a new line for each object with indent:
public string GetText(Object obj, int indentLevel)
{
string text = "";
string indentation = new string(' ', indentLevel * 8);
text += indentation + obj.name;
if (obj.depandantObj != null && obj.depandantObj.Count > 0)
{
indentLevel++;
foreach (Object o in obj.depandantObj)
{
text += Environment.NewLine + GetText(o, indentLevel);
}
}
else
return text;
return text;
}
Call the method for each object in the list and write the text into the text file at the end:
make sure both of the fields (name and depandantObj) in Object class are public
List<Object> objects;
//add items to list
...
if(objects != null)
{
string text = "";
foreach (Object obj in objects)
{
text += GetText(obj, 0);
}
File.WriteAllText(Server.MapPath("~/sample.txt"), text);
}
First create your Objects with nested list forms. For example:
public class MyObject{
// some properties here
}
public class MySecondObject{
List<MyObject> {get; set;}
}
public class MythirdObject{
List<MySecondObject> {get; set;}
}
And when you want to save the data to a file just seriliaze them to json , it will already create a readable json file for you.
// I assume you can create your data or fetch them
var data = List<MyThirdObject> ();
string json = JsonConvert.SerializeObject(_data);
//write string to file
System.IO.File.WriteAllText(#"D:\path.txt", json);
If you don't want json than you can create a recusrive method that add each object to under last one.
Have a look this question for how you can this at this way.
Consider using json, download newtonsoft dll from Nuget.
a code example:
public class MyObject
{
public string Name { get; set; }
}
public class MySecondObject
{
public List<MyObject> DepObj { get; set; } = new List<MyObject>();
}
usage example:
MyObject obj = new MyObject
{
Name = "example"
};
MySecondObject mySecond = new MySecondObject();
mySecond.DepObj.Add(obj);
var data = new List<MySecondObject>
{
mySecond
};
string json = JsonConvert.SerializeObject(data, Formatting.Indented);
System.IO.File.WriteAllText(#"D:\file.txt", json);
File content:
[
{
"DepObj": [
{
"Name": "example"
}
]
}
]
First, let's elaborate initial Object class; I've renamed it (in order not to conflict with System.Object), make fields being public, add a constructor:
class MyObject {
public string name = "";
public List<MyObject> depandantObj = new List<MyObject>();
public MyObject(string value, params MyObject[] dependent) {
name = value;
if (dependent != null)
foreach (var item in dependent)
depandantObj.Add(item);
}
}
Then we can implement an iterator, IEnumerable<string>:
private static IEnumerable<string> MyObjectToTree(IEnumerable<MyObject> roots, int shift = 6) {
if (null == roots)
yield break;
foreach (var root in roots) {
// We don't want infinte loop if objects create a cycle
HashSet<MyObject> completed = new HashSet<MyObject>();
Stack<Tuple<int, MyObject>> agenda = new Stack<Tuple<int, MyObject>>();
agenda.Push(Tuple.Create(0, root));
while (agenda.Any()) {
Tuple<int, MyObject> item = agenda.Pop();
if (!completed.Add(item.Item2))
continue;
List<MyObject> children = item.Item2?.depandantObj ?? new List<MyObject>();
children.Reverse();
yield return $"{new string(' ', shift * item.Item1)}{item.Item2?.name}{(children.Any() ? ":" : "")}";
foreach (var child in children)
agenda.Push(Tuple.Create(item.Item1 + 1, child));
}
}
}
Demo:
// I've added the MyObject constructor for this readable creation
List<MyObject> objects = new List<MyObject>() {
new MyObject("object1",
new MyObject("object2"),
new MyObject("object3",
new MyObject("object4"),
new MyObject("object5"))),
new MyObject("object6",
new MyObject("object2")),
};
foreach (string line in MyObjectToTree(objects, 6))
Console.WriteLine(line);
// If you want to write into file:
// File.WriteAllLines(#"c:\MyFile.txt", MyObjectToTree(objects, 6));
Outcome:
object1:
object2
object3:
object4
object5
object6:
object2

dynamic .ToObject<CustomClass>() not working as expected

I have a .json file and a custom class.
I am taking this .json file and putting it in a dynamic variable, so that I can access specific points in the file at run time. See below code
private static dynamic elements = null;
public static dynamic Elements { get { return elements; } }
static Settings()
{
elements = JObject.Parse(Common.GetFile("Elements.json"));
}
In the below function, I am using the dynamic variable above in order to identify smaller "chunks" of the .json file. [See Below]
public void Login(string pUserName, string pPassword)
{
dynamic _module = Settings.Elements.Login;
ElementObject _userName = _module.UserName.ToObject<ElementObject>();
ElementObject _password = _module.Password.ToObject<ElementObject>();
ElementObject _loginBTN = _module.LoginButton.ToObject<ElementObject>();
_userName.OnSendKeys(pUserName);
_password.OnSendKeys(pPassword);
_loginBTN.OnClick();
}
The issue, is that ElementObject.cs has a constructor that requires the public properties to be populated via the .json script. However, when stepping through debugging, the public properties arn't getting set until after the variable declaration. [See images below]
public class ElementObject
{
public string ClassName;
public string CssSelector;
public string Id;
public string LinkText;
public string Name;
public string PartialLinkText;
public string TagName;
public string XPath;
private int index = 0;
private string finalName = "";
private string finalClassName = "";
public ElementObject()
{
var _b = new string[] { nameof(ClassName), nameof(CssSelector), nameof(Id), nameof(LinkText), nameof(Name), nameof(PartialLinkText), nameof(TagName), nameof(XPath) };
var _a = new string[] { ClassName, CssSelector, Id, LinkText, Name, PartialLinkText, TagName, XPath };
index = Array.IndexOf(_a, _a.FirstOrDefault(s => !string.IsNullOrEmpty(s)));
finalName = _a[index];
finalClassName = _b[index];
}
}
In the picture below, you can see that I am properly getting the json data.
In the below picture, by the time we get to the constructor, none of the values are being populated
In the below picture, you can see that after we stepped out of the constructor, the properties were applied, but the constructor didn't see it applied.
I created a work around, after investigation what I wanted doesn't seem to work.
Here is my work around. [See Code Below].
public ElementObject() { }
public static ElementObject Create(dynamic pSrcObj)
{
ElementObject obj = pSrcObj.ToObject<ElementObject>();
obj.Init();
return obj;
}
public void Init()
{
var _b = new string[] { nameof(ClassName), nameof(CssSelector), nameof(Id), nameof(LinkText), nameof(Name), nameof(PartialLinkText), nameof(TagName), nameof(XPath) };
var _a = new string[] { ClassName, CssSelector, Id, LinkText, Name, PartialLinkText, TagName, XPath };
index = Array.IndexOf(_a, _a.FirstOrDefault(s => !string.IsNullOrEmpty(s)));
finalName = _a[index];
finalClassName = _b[index];
}
In order for me now to create the object, i create it like this;
ElementObject _userName = ElementObject.Create(_module.UserName);

Handling data with lists

Is this bad practice? Or is it completely fine to do this
private readonly List<KeyValuePair<GameType, KeyValuePair<string, string>>> _tempStats = new List<KeyValuePair<GameType, KeyValuePair<string, string>>>();
GameType is an enumerator by the way.
I have data that I am downloading from a table that has a few different GameTypes with two strings associated with them. So it will parse the data and determines what GameType to assign it, and it finds the tables Key and it's Value. And it works, it stores the information and I am able to retrieve it with no problems, but it just seems like having a list of a KeyValuePair with a KeyValuePair isn't right, but maybe it is. Would using a tuple be a better approach?
My current usage of the list
private void ParseNodeText(string nText, GameType gmode)
{
_tempStats.Clear();
var reader = new StringReader(nText);
while((nText = reader.ReadLine()) != null)
{
nText = nText.Replace(" ", "");
if (nText == "")
{
continue;
}
string statType = Regex.Replace(nText, "[^A-Za-z]", "");
string statValue = Regex.Replace(nText, "[^0-9]", "");
// Console.WriteLine(gmode + " : Found line with Type of {0} and a value of {1}",statType,statValue);
_tempStats.Add(new KeyValuePair<GameType, KeyValuePair<string, string>>(gmode, new KeyValuePair<string, string>(statType, statValue)));
}
}
Your Solution is fine, but not that good readable.
You could do something like this:
private readonly List<GameStats> _tempStats = new List<GameStats>();
and GameStats is a own written simple Class:
public class GameStats
{
public GameType Type { get; set; }
public string StatType { get; set; }
public string StatValue { get; set; }
public GameStats(GameType gameType, string statType, string statValue)
{
this.Type = gameType;
this.StatType = statType;
this.StatValue = statValue;
}
}
and add it to your list like:
_tempStats.Add(new GameStats(gmode, statType, statValue));
i think, this looks good and will do it.

Create nested objects dynamically

I am using third party library where if I wanted to created nested structure for directories I have to create like this
new ClassA("folder1", new ClassA("folder2", new ClassA("folder3")));
this will create folder structure like this folder1->folder2->folder3.
To make it simple for my users I am creating methods where users pass the path as parameter and my method process the path and should create the above object structure which internally creates folder structure.
right now I am able to parse the path like a tree but could not able to create above object structure.
This is the sample console application code
class Program
{
static void Main(string[] args)
{
List<string> Paths = new List<string>();
Paths.Add("D1");
Paths.Add("E1");
Paths.Add(#"E1\E11");
Paths.Add(#"D1\D11");
Paths.Add(#"D1\D12");
Paths.Add(#"D1\D2");
Paths.Add(#"D1\D2\D21");
Node nodeObj = new Node();
foreach (var path in Paths)
{
nodeObj.AddPath(path);
}
//var nodes = nodeObj.Nodes;
Node current = nodeObj;
int level = 0;
ReadNodes(current, level);
}
private static void ReadNodes(Node current, int level)
{
foreach (string key in current.Nodes.Keys)
{
var tablevel = level;
string tab = string.Empty;
while (tablevel>0)
{
tab = tab + " ";
tablevel--;
}
Console.WriteLine(tab +":" + key);
// The child node.
Node child;
if (current.Nodes.TryGetValue(key, out child) && child.Nodes.Count>0)
{
ReadNodes(child, level+1);
}
else { }
}
}
}
public class Node
{
private readonly IDictionary<string, Node> _nodes =
new Dictionary<string, Node>();
public IDictionary<string, Node> Nodes
{
get { return _nodes; }
}
private string Path { get; set; }
public string Source { get; set; }
public void AddPath(string path)
{
char[] charSeparators = new char[] { '\\' };
// Parse into a sequence of parts.
string[] parts = path.Split(charSeparators,
StringSplitOptions.RemoveEmptyEntries);
// The current node. Start with this.
Node current = this;
// Iterate through the parts.
foreach (string part in parts)
{
// The child node.
Node child;
// Does the part exist in the current node? If
// not, then add.
if (!current._nodes.TryGetValue(part, out child))
{
// Add the child.
child = new Node
{
Path = part
};
// Add to the dictionary.
current._nodes[part] = child;
}
// Set the current to the child.
current = child;
}
}
}
class Test : yourClass
{
string name;
Test childTest;
public Test(string name)
{
this.name = name;
this.childTest = null;
}
public Test(string name, Test test)
{
this.name = name;
this.childTest = test;
}
}
Test a = new Test("a", new Test("b", new Test("c")));
Are you just searching for a structure like this? You need two constructors one that accepts only a string and one that accepts a string and another class of the same type

Create a object hierarchy from a list of folder locations

I have a list of locations as strings;
locA/locB
locA/locB/locH
locC/locD/locE
locC/locD/locE/locK
locF/locG
I've been trying to create an object that uses the same structure as the list of locations passed to it;
e.g. Something like..
var myHObject=CreateHeirarchicalObjectFromList(myStringListOfLocations);
I'm having problems looping through the list without almost doing it manually with loads of loops. Is there an easier way, maybe recursion?
I want to end up with an object like this;
.locA
.locB
.locH
.locC
.locD
.locE
.locK
.locF
.locG
That I can use to create a visual hierarchy.
Prob not the best but knocked up in LinqPad, will reformat in a sec..
void Main()
{
var strings = new string[]{"locA/locB","locA/locB/locH",
"locC/locD/locE","locC/locD/locE/locK","locF/locG"};
var folders = Folder.Parse(strings);
folders.Dump();
}
public class Folder
{
public string Name { get; set; }
public List<Folder> Folders { get; internal set; }
public Folder()
{
Folders = new List<Folder>();
}
//Presume that each string will be folder1/folder2/folder3
public static IEnumerable<Folder> Parse(IEnumerable<string> locations)
{
var folders = new List<Folder>();
foreach (string location in locations)
{
string[] parts = location.Split('/');
Folder current = null;
foreach (string part in parts)
{
var useFolders = current != null ?
current.Folders : folders;
current = useFolders.SingleOrDefault(f => f.Name == part) ?? new Folder() { Name = part };
if (!useFolders.Contains(current)) { useFolders.Add(current); }
}
}
return folders;
}
}

Categories

Resources