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

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);

Related

In my main, how to access string array in an array object? How to access parameter from constructor?

UML is attached. I want to create a readonly property of pre which is an array of string. When I create an object in the main and try to set name and pre it is showing me an error.
UML
using System;
class Unit
{
private string _name;
private string[] _pre;
public Unit(string name, string[] pre)
{
_name = name;
_pre = new string[2];
}
public string Name { get { return _name; } }
public string[] Pre { get { return _pre; } }
}
class Program
{
public static void DisplayInfo(Unit[] _u)
{
for (int i = 0; i < 2; i++)
{
Console.WriteLine(_u[i].Name + _u[i].Pre);
}
}
static void Main(string[] args)
{
Unit[] unitarraytest = new Unit[2];
unitarraytest[0] = new Unit("test 1", "test 3");
unitarraytest[1] = new Unit("test 2", "test 4");
DisplayInfo(unitarraytest);
}
}
Your example makes little sense. You Unit constructor takes a parameter for "Pre", but immediately throws it away and allocates a new empty string array instead. It should probably be written like
class Unit
{
public Unit(string name, string[] pre)
{
Name = name;
Pre = pre;
}
public string Name { get;}
public string[] Pre { get;}
}
When creating Unit objects you actually need to create an array for the "Pre" parameter. Like new Unit("Name", new []{"pre1", "pre2"});
And when outputting the strings you need to access the individual strings in the array, or combine them to a larger string, for example like Console.WriteLine(_u[i].Name + string.Join(" , ", _u[i].Pre));

Accessing a string from an if function inside of the returned program

Pretty much, I want to use the variable 'username' in the program that the if function returns, however this program is located in my program.cs, and the if function is not.
Wasn't able to get the public static string in the if function, maybe this is obvious to experienced coders but I'm a beginner.
I tested it out by having the console print the variable value, with no success.
The if function:
public class WeeWoo : ChatBot
{
public static Dictionary<string, string> whitelisted;
static WeeWoo()
{
string json = File.ReadAllText("whitelists/walls.json");
var data = JsonConvert.DeserializeObject<dynamic>(json);
whitelisted = data.ToObject<Dictionary<string, string>>();
}
public override void GetTextAsync(string text)
{
string message = "";
string username = "";
text = GetVerbatim(text);
if (text.Contains("-> me"))
{
text = text.Replace("[", String.Empty).Replace("]", String.Empty).Replace(" -> me", String.Empty);
String[] args = text.Split(' ');
username = args[1];
message = args[2];
if (message.Equals("weewoo") && (whitelisted.ContainsKey(username)))
{
Program.WeeWoo();
}
}
}
public static string username;

How to set localappdata path properly, not showing the full path(Path.Combine & Environment.GetFolderPath)

I'm trying to Path.Combine, but having highlighted string(appdatapath), helper say's that "a field initializer cannot reference the non-static field, method, or property'MySuperAPP.appdatapath' "
the code is :
string appdatapath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string path = Path.Combine(appdatapath, "second/part/of/folderpath");
what i want is:
string path = "C:/Users/USER/AppData/Local/Some/Dir/"
what i tried :
string static appdatapath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string static path = Path.Combine(appdatapath,"second/part/of/folderpath").ToString;
and
public static string GetMyLocalAppDir()
{
return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData).ToString();
}
string path = Path.Combine(GetMyLocalAppDir(),"second/part/of/folderpath").ToString;
i think the variants that i'm tried may be wrong..)
need your advice) thank's!)
When you initialize a field (this mean: when you provide a dynamic field with a value at runtime) it must be a static value.
Therefore you must declare "appdatapath" as static.
public partial class MainWindow : Window
{
private static string appdatapath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
private (static) string path = System.IO.Path.Combine(appdatapath, "second/part/of/folderpath"); //make this static if you want that this field can't be changed.
public MainWindow()
{
InitializeComponent();
}
}
Also make sure your declaration is in the right order:
public or private static or not type e.g. string name of variable
Finaly: if you have more directory's to combine, put eacht part separately:
Path.Combine(appdatapath, "second", "part", "of", "folderpath")
public static void Read_bootup3_file()
{
qq = 0;
string downloadz2;
string fileNameSDcard = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "download.txt");
string CurrentContents;
CurrentContents = fileNameSDcard;
//CurrentContents = File.ReadAllText(fileNameSDcard);
File.WriteAllText(fileNameSDcard, CurrentContents);
using (StreamReader sr = new StreamReader(fileNameSDcard))
{
downloadz2 = sr.ReadToEnd();
}
//downloadz = downloadz2.ToCharArray();
qq = 0;
for (leep = 0; leep <= lotsize; leep++)
{
for (lep = 0; lep <= 20; lep++)
{
Tester.garage_array_database[lep, leep] = downloadz[qq].ToString();
qq++;
}
}

Read values from a non-delimited string into class object

I have a string with the following structure:
Student Name________AgeAddress_______________________Bithday___Lvl
Example:
Jonh Smith 016Some place in NY, USA 01/01/2014L01
As you can see, there is no delimited character like | or ,
Also, there is no space between fields (if you check, there is no space between Age/Address and Birthday/Level.
The size of each field is static so if data's length is less then it will contains white spaces.
I have a class that need to be filled with that information:
public class StudentData
{
public char[] _name = new char[20];
public string name;
public char[] _age = new char[3];
public string age;
public char[] _address = new char[30];
public string address;
public char[] _bday = new char[10];
public string bday;
public char[] _level = new char[3];
public string level;
}
Is there any way to do this automatically and dynamically?
I mean I really don't want to code like this:
myClass.name = stringLine.substring(0,19);
myClass.age = stringLine.substring(20,22);
That's because I have way more fields that the ones added in this example & way more string lines with other different data.
Update: There were supposed to be a lot of spaces between "Smith" and "016", but I don't know how to edit it.
Update2: If I use StringReader.Read() I can evade to use substring and indexes, but it isn't still so dynamically because I would need to repeat those 3 lines for each field.
StringReader reader = new StringReader(stringLine);
reader.Read(myClass._name, 0 myClass._name.Length);
myClass.name = new string(myClass._name);
Given your requirement I came up with an interesting solution. All be-it it may be more complex and longer than using the String.SubString() method as stated.
However this solution is transferable to other types and other string. I used a concept of Attributes, Properties, and Reflection to parse a string by a Fixed Length and setting the class Properties.
Note I did change your StudentData class to follow a more conventional coding style. Following this handy guide on MSDN: http://msdn.microsoft.com/en-us/library/xzf533w0(v=vs.71).aspx
Here is the new StudentData class. Note it uses the properties as opposed to fields. (Not discussed here).
public class StudentData
{
string name;
string age;
string address;
string bday;
string level;
[FixedLengthDelimeter(0, 20)]
public string Name { get { return this.name; } set { this.name = value; } }
[FixedLengthDelimeter(1, 3)]
public string Age { get { return this.age; } set { this.age = value; } }
[FixedLengthDelimeter(2, 30)]
public string Address { get { return this.address; } set { this.address = value; } }
[FixedLengthDelimeter(3, 10)]
public string BDay { get { return this.bday; } set { this.bday = value; } }
[FixedLengthDelimeter(4, 3)]
public string Level { get { return this.level; } set { this.level = value; } }
}
Note on each of the properties there is an Attribute called FixedLengthDelimeter that takes two parameters.
OrderNumber
FixedLength
The OrderNumber parameter denotes the order in the string (not the position) but the order in which we process from the string. The second parameter denotes the Length of the string when parsing the string. Here is the full attribute class.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class FixedLengthDelimeterAttribute : Attribute
{
public FixedLengthDelimeterAttribute(int orderNumber, int fixedLength)
{
this.fixedLength = fixedLength;
this.orderNumber = orderNumber;
}
readonly int fixedLength;
readonly int orderNumber;
public int FixedLength { get { return this.fixedLength; } }
public int OrderNumber { get { return this.orderNumber; } }
}
Now the attribute is simple enough. Accepts the two paramters we discussed eariler in the constructor.
Finally there is another method to parse the string into the object type such as.
public static class FixedLengthFormatter
{
public static T ParseString<T>(string inputString)
{
Type tType = typeof(T);
var properties = tType.GetProperties(BindingFlags.Instance | BindingFlags.Public); //;.Where(x => x.GetCustomAttributes(typeof(FixedLengthDelimeterAttribute), false).Count() > 0);
T newT = (T)Activator.CreateInstance(tType);
Dictionary<PropertyInfo, FixedLengthDelimeterAttribute> dictionary = new Dictionary<PropertyInfo, FixedLengthDelimeterAttribute>();
foreach (var property in properties)
{
var atts = property.GetCustomAttributes(typeof(FixedLengthDelimeterAttribute), false);
if (atts.Length == 0)
continue;
dictionary[property] = atts[0] as FixedLengthDelimeterAttribute;
}
foreach (var kvp in dictionary.OrderBy(x => x.Value.OrderNumber))
{
int length = kvp.Value.FixedLength;
if (inputString.Length < length)
throw new Exception("error on attribute order number:" + kvp.Value.OrderNumber + " the string is too short.");
string piece = inputString.Substring(0, length);
inputString = inputString.Substring(length);
kvp.Key.SetValue(newT, piece.Trim(), null);
}
return newT;
}
}
The method above is what does the string parsing. It is a pretty basic utility that reads all the properties that have the FixedLengthDelimeter attribute applied a Dictionary. That dictionary is then enumerated (ordered by OrderNumber) and then calling the SubString() method twice on the input string.
The first substring is to parse the next Token while the second substring resets the inputString to start processing the next token.
Finally as it is parsing the string it is then applying the parsed string to the property of the class Type provided to the method.
Now this can be used simply like this:
string data1 = "Jonh Smith 016Some place in NY, USA 01/01/2014L01";
StudentData student = FixedLengthFormatter.ParseString<StudentData>(data1);
What this does:
Parses a string against property attributes in a fixed length format.
What this does not do:
It does convert the parsed strings to another type. Therefore all the properties must be a string. (this can be easily adapted by adding some type casting logic in).
It is not well tested. This is only tested against a few samples.
It is not by all means the only or best solution out there.
You could use FileHelpers library (NuGet).
Just define the structure of your input file with attributes:
[FixedLengthRecord]
public class StudentData
{
[FieldFixedLength(20)]
[FieldTrim(TrimMode.Right)]
public string name;
[FieldFixedLength(3)]
public string age;
[FieldFixedLength(30)]
[FieldTrim(TrimMode.Right)]
public string address;
[FieldFixedLength(10)]
public string bday;
[FieldFixedLength(3)]
public string level;
}
Then simply read the file using FileHelperEngine<T>:
var engine = new FileHelperEngine<StudentData>();
var students = engine.ReadFile(filename);

C# custom object in combobox

I am relatively new to C# (WinForms), and had a question regarding combo boxes. I have a combo box of Reviewer objects (it is a custom class with an overridden ToString method) and am currently attempting to go through all the checked items and use them to generate a setup file.
Here is how the combo box is populated (populated on form load). Parameters is just a collection of linked lists and parsing code.
for (int i = 0; i < parameters.GetUsers().Count; i++)
{
UserList.Items.Add(parameters.GetUsersArray()[i], parameters.GetUsersArray()[i].isSelected());
}
Here is how I am trying to read it. setup is a StringBuilder. The problem is that GetID is not defined. Does the add function above cast the Reviewer object to a Object object? It looks a little funny since it creates a file fed into a Perl script. A sample desired output line looks like this: inspector0 => "chg0306",
for (int i = 0; i < UserList.CheckedItems.Count; i++)
{
setup.AppendLine("inspector" + i.ToString() + " => \t \"" +
UserList.CheckedItems[i].GetID() + "\",");
}
Here is the users class: (Sample User is ID = aaa0000 name: Bob Joe)
public class Reviewer
{
private string name;
private string id;
private bool selected;
public Reviewer(string newName, string newID, bool newSelected)
{
name = newName;
id = newID;
selected = newSelected;
}
public string GetName()
{
return name;
}
public override string ToString()
{
//string retVal = new string(' ', id.Length + name.Length + 1);
string retVal = id + '\t' + name;
return retVal;
}
public string GetID()
{
return id;
}
public bool isSelected()
{
return selected;
}
}
For posterity, here is the Parameters class:
public class ParameterLists
{
public ParameterLists()
{
projects = new LinkedList<string>();
reviewers = new LinkedList<Reviewer>();
}
public enum FileContents {
PROJECT_LIST,
USERS_LIST,
}
public LinkedList<Reviewer> GetUsers()
{
return reviewers;
}
public LinkedList<string> GetProjects()
{
return projects;
}
public Reviewer[] GetUsersArray()
{
Reviewer[] userArray = new Reviewer[reviewers.Count];
reviewers.CopyTo(userArray, 0);
return userArray;
}
public string[] GetProjectsArray()
{
String[] projectArray = new String[projects.Count];
projects.CopyTo(projectArray, 0);
return projectArray;
}
public void LoadParameters(string fileName)
{
//Reads the parameters from the input file.
}
private void CreateDefaultFile(string fileName)
{
// Create the file from the defaultfile , if it exists.
// Otherwise create a blank default file.
}
private LinkedList <string> projects;
private LinkedList <Reviewer> reviewers;
}
I am probably missing something simple, coming from embedded C++. Any help would be appreciated.
You have to cast that object:
((Reviewer)UserList.CheckedItems[i]).GetID()

Categories

Resources