I have a method which gets the values of the properties of an object and appends some commas to it. I want to make this generinc so i can use it with other objects.
foreach (var row in rows.ToList())
{
sbResult.Append(
delimiter + row.MediaName + delimiter + separator +
delimiter + row.CountryName + delimiter + separator +
delimiter + row.ItemOverRideDate + delimiter + separator +
delimiter + row.Rating + delimiter + separator +
delimiter + row.BatchNo + delimiter + separator +
delimiter + row.NoInBatch + delimiter + separator +
delimiter + row.BatchDate + delimiter + separator +
delimiter + row.DataType + delimiter + separator +
delimiter + row.ByLine + delimiter + separator +
delimiter + row.IssueNo + delimiter + separator +
delimiter + row.Issue + delimiter + separator +
delimiter + row.MessageNo + delimiter + separator +
delimiter + row.Message + delimiter + separator +
delimiter + row.SourceName + delimiter + separator +
delimiter + row.SourceType + delimiter + separator);
//end of each row
sbResult.AppendLine();
}
I have tried using var rowData = row.GetType().GetProperties(); but it only returns the property itself and I dont know how to get the value of the property.
Since Type.GetProperties returns a collection of PropertyInfo, you follow that up by calling PropertyInfo.GetValue. Here's how you can do that (and all the rest together) with LINQ:
var line = string.Join(
row.GetType().GetProperties()
.Select(pi => pi.GetValue(row))
.Select(v => delimiter + v.ToString() + delimiter),
separator);
However, you might want to reconsider your approach. This code will break if GetProperties fetches static properties or indexers along with "normal" properties; it also requires that the code be run with full trust (otherwise no reflection is possible). And finally, it's going to be slow because a) reflection is inherently slow and b) it will keep reflecting on the same things over and over again without caching any of the information it has already discovered.
In addition to the above potential problems, if there is even a remote chance that you will later want to filter what gets printed out it is probably better to encapsulate this logic inside a (virtual?) method on row and just do something like
sbResult.AppendLine(row.SerializeAsLine());
You can use something like this to iterate over all the properties of a particular type:
public static IEnumerable<KeyValuePair<string, T>> PropertiesOfType<T>(object obj)
{
return from p in obj.GetType().GetProperties()
where p.PropertyType == typeof(T)
select new KeyValuePair<string, T>(p.Name, (T)p.GetValue(obj));
}
Then you could specify the type as string for all your string properties.
Compilable sample:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
private static void Main()
{
var test = new Test
{
Str1 = "S1",
Str2 = "S2",
Str3 = "S3",
Str4 = "S4"
};
foreach (var property in PropertiesOfType<string>(test))
{
Console.WriteLine(property.Key + ": " + property.Value);
}
}
public static IEnumerable<KeyValuePair<string, T>> PropertiesOfType<T>(object obj)
{
return from p in obj.GetType().GetProperties()
where p.PropertyType == typeof(T)
select new KeyValuePair<string, T>(p.Name, (T)p.GetValue(obj));
}
}
public class Test
{
public string Str1 { get; set; }
public string Str2 { get; set; }
public string Str3 { get; set; }
public string Str4 { get; set; }
}
}
Here it is.
List<PropertyInfo> _propInfo = _row.GetType().GetProperties();
foreach (var item in _propInfo)
{
object _value = item.GetValue(_row, null);
if (_value != null)
{
// Save the Value
}
}
GetProperties returns an array of PropertyInfo, so use the GetValue method and use your object as it's input to get the value for each property. Here is the code:
public class MyClass
{
public string MyProperty1 { get; set; }
public string MyProperty2 { get; set; }
public string MyProperty3 { get; set; }
}
Then
MyClass myObj = new MyClass() { MyProperty1 = "first", MyProperty2 = "second", MyProperty3 = "third" };
List<string> array = new List<string>();
foreach (var item in typeof(MyClass).GetProperties())
{
array.Add(item.GetValue(myObj, null).ToString());
}
var result = string.Join(",", array); //use your own delimiter
var values = instance
.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Select(z => string.Format("{0}: {1}\n", z.Name, z.GetValue(instance, null)));
string res = string.Concat(values);
Where instance is the instance of your object. You might want to avoid LINQ and use a loop if StringBuilder is required (depending on the number of properties).
Related
I am trying to write a C# program using Visual Studio that reads data from a file and allow me perform various sorts on the data. I am a beginner so it took much research to figure out how to write this code: Now I get a:
Format Exception was unhandled. Input string was not in a correct
format
I am not sure where I went wrong. This is happening for the following line of code:
Candidate newrec = new Candidate(str[0], str[1], str [2], str[3], str[4], str[5], str[6], Convert.ToInt32(str[7]), str[8], str[9], str[10], str[11]);
The entire code is as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Collections;
namespace Unit4IP
{
//used to sort in asceding and descending order
public struct Candidate:IComparable
{
public char[] _FirstName;
public char[] _LastName;
public char[] _Company;
public char[] _Address;
public char[] _City;
public char[] _Country;
public char[] _State;
public char[] _Phone;
public char[] _Fax;
public char[] _Email;
public char[] _Web;
public int _zip;
//for comparing objects
public int CompareTo(object obj)
{
Candidate Candidate2 = (Candidate)obj;
return _LastName.ToString().CompareTo(Candidate2._LastName.ToString());
}
//implements sorting based on assignments such as zip, lastname, etc.
public int CompareTo(Candidate Candidate2,
CandidateComparer.ComparisonType comptype)
{
if(comptype==CandidateComparer.ComparisonType.Lastname)
{
String _LName = new String(_LastName);
String LName = new String(Candidate2._LastName);
return _LName.CompareTo(LName);// Convert Character Array to String because CompareTo Works efficiently with Strings
}
else
{
return Candidate2._zip.CompareTo(_zip); // compareto values that are interchanged in descending order
}
}
//Constructor of Candidate Structure
public Candidate(string FirstName, string LastName, string Company, string Address, string City, string Country, string State, int zip, string Phone, string Fax, string Email, string Web)
{
_FirstName = new char[12];
_LastName = new char[16];
_Company = new char[32];
_Address = new char[32];
_City = new char[24];
_Country = new char[24];
_State = new char[2];
_Phone = new char[12];
_Fax = new char[12];
_Email = new char[32];
_Web = new char[42];
_FirstName = FirstName.ToCharArray();
_LastName = LastName.ToCharArray();
_Company = Company.ToCharArray();
_Address = Address.ToCharArray();
_City = City.ToCharArray();
_Country = Country.ToCharArray();
_State = State.ToCharArray();
_zip = zip;
_Phone = Phone.ToCharArray();
_Fax = Fax.ToCharArray();
_Email = Email.ToCharArray();
_Web = Web.ToCharArray();
}
//Implement IComparer Interface as nested structure
public struct CandidateComparer : IComparer
{
public enum ComparisonType
{ Lastname = 1, zip = 2 }
private ComparisonType _comparisonType;
public ComparisonType comptype
{
get { return _comparisonType; }
set { _comparisonType = value; }
}
public int Compare(object x, object y)
{
Candidate Candidate1 = (Candidate)x;
Candidate Candidate2 = (Candidate)y;
return Candidate1.CompareTo(Candidate2, _comparisonType);
}
}
}
class Program
{
static void Main(string[] args)
{
ArrayList ArrayTest = new ArrayList();
//Loading of File 'ITCO321_U4_sample_data.csv' into ArraList. File only holds values, no heading i.e. remove headings
StreamReader stream1 = File.OpenText("c:\\Users\\Cdhss\\Documents\\ITCO321_U4IP_sample_data-2.csv");
string recdata = null;
while ((recdata = stream1.ReadLine()) != null)
{
string[] str = recdata.Split(',');
Candidate newrec = new Candidate(str[0], str[1], str [2], str[3], str[4], str[5], str[6], Convert.ToInt32(str[7]), str[8], str[9], str[10], str[11]);
ArrayTest.Add(newrec);//add struct object into ArrayList
}
//Traversing of Records
Console.WriteLine("Traversing Records");
foreach (Candidate Candidate1 in ArrayTest)
{
string fname = new String(Candidate1._FirstName);
string lname=new String(Candidate1._LastName);
string company = new String(Candidate1._Company);
string address=new String(Candidate1._Address);
string city=new String(Candidate1._City);
string country = new String(Candidate1._Country);
string phone = new String(Candidate1._Phone);
string fax = new String(Candidate1._Fax);
string email=new String(Candidate1._Email);
string web = new String(Candidate1._Web);
Console.WriteLine( fname + "," + lname + "," + company + "," + address + "," + city + "," + country + "," + Candidate1._zip + "," + phone + "," + fax + "," + email + "," + web);
}
Candidate.CandidateComparer comparer = new Candidate.CandidateComparer();
//Sort by Lastname in ascending order
comparer.comptype = Candidate.CandidateComparer.ComparisonType.Lastname;
ArrayTest.Sort(comparer);
Console.WriteLine("Sorting of Elements by LastName");
foreach (Candidate Candidate1 in ArrayTest)
{
string fname = new String(Candidate1._FirstName);
string lname = new String(Candidate1._LastName);
string company = new String(Candidate1._Company);
Console.WriteLine("\t" + fname + "," + lname + "," + company);
}
// Data sorted in desending order of ZIP field
comparer.comptype = Candidate.CandidateComparer.ComparisonType.zip;
ArrayTest.Sort(comparer);
Console.WriteLine("Sorting of Elements by Zip");
foreach (Candidate Candidate1 in ArrayTest)
{
string fname = new String(Candidate1._FirstName);
string lname = new String(Candidate1._LastName);
string company = new String(Candidate1._Company);
Console.WriteLine("\t" + fname + "," + lname + "," + company + "," + Candidate1._zip);
}
//Display Records of 'NY' State
Console.WriteLine("Display Records of NY State");
foreach (Candidate Candidate1 in ArrayTest)
{
string fname = new String(Candidate1._FirstName);
string lname = new String(Candidate1._LastName);
string company = new String(Candidate1._Company);
string address = new String(Candidate1._Address);
string city = new String(Candidate1._City);
string country = new String(Candidate1._Country);
string phone = new String(Candidate1._Phone);
string fax = new String(Candidate1._Fax);
string email = new String(Candidate1._Email);
string web = new String(Candidate1._Web);
if (new String(Candidate1._State).Contains("NY"))
Console.WriteLine(fname + "," + lname + "," + company + "," + address + "," + city + "," + country + "," + Candidate1._zip + "," + phone + "," + fax + "," + email + "," + web);
}
Console.Read();
}
}
}
Your problem is in your call to Convert.ToInt32. Whatever string you are passing can't be parsed as an int.
A good solution here might be to use the some structured exception handling, either with try catch blocks or Int32.TryParse(string) for example you could do this
int intValue;
if (Int32.TryParse(str[7], out intValue))
{
//Do some thing with the int value
}
else
{
throw new Exception("Some informative error message, probably using string.Format to include the string you tried to parse");
}
TryParse returns true if the parse is successfull, false if it is not, and puts the parsed integer value in the second out parameter.
A few other comments on you code to help you out.
No one uses ArrayLists in C# anymore. Generics were introduced a long time ago, and it is almost always better, from a standpoint of both type safety and speed to use a List (Look up C# generics if you don't know what they are).
You don't need to copy the candidate properties in your foreach loop into new variables.
Give more informative variable names. Readability is the single most important quality in good code. fname should be firstName, recdata should be recievedData. Key strokes are cheap in a world with tab completion.
In my database, I store phone numbers like this "7279884545". One solid string with no spaces.
In my class that stores the phone number info, I have a function that will add the correct punctuation.
public static String beautifyPhoneNumber(String number, String extension)
{
String beautifulNumber = "";
if (!String.IsNullOrEmpty(number))
{
beautifulNumber = "(" + number.Substring(0, 3) + ") " +
number.Substring(3, 3) + "-" +
number.Substring(6, 4);
}
if (!String.IsNullOrEmpty(extension))
{
beautifulNumber += " x" + extension;
}
return beautifulNumber;
}
And here is how I have the variable in the class itself.
private string _PhonePrimary;
[DisplayName("Phone Primary")]
public string PhonePrimary
{
get
{
if(this._PhonePrimary != null)
{
this._PhonePrimary = beautifyPhoneNumber(this._PhonePrimary, this.Extension);
}
return this._PhonePrimary;
}
set
{
this._PhonePrimary = value;
}
}
This works fine most of the time. The numbers are outputted to the screen in a "(727) 988-4545" or "(727) 988-4545 x12" if there is an extension for that record in the database.
The problem comes when I do a HttpPost request. The model information that is inside of the post request looks like this.
_PhonePrimary = "(727) 988-4545"
PhonePrimary = "(((7) 2) -7) -"
As noted, it looks like you're calling beautifyPhoneNumber on a number you've already beautified.
Here's an implementation using regular expressions that should get you started:
public static String BeautifyPhoneNumber(string numberToBeautify)
{
//The below gives us capture groups for each
//individual piece of the number.
var regularExpression = new Regex(#"(\d{3})(\d{3})(\d{4})(x\d*)?");
//This matches a number that's already been beautified,
//so we can guard against beautifying twice.
var alreadyBeautifulExpression = new Regex(#"(\(\d{3}\)) (\d{3})-(\d{4}) ?(x\d*)?");
var beautifulNumber = string.Empty;
var separator = "-";
var space = " ";
//This prevents us from accidentally beautifying
//something more than once
//You could also guard against this in your getter using a
//IsBeautified extension, using the alreadyBeautifulExpression above
if (alreadyBeautifulExpression.IsMatch(numberToBeautify))
{
return numberToBeautify;
}
//Trying to protect against invalid input... May be insufficient,
//Or unnecessary
if (string.IsNullOrEmpty(numberToBeautify)
|| regularExpression.Matches(numberToBeautify).Count <= 0)
{
return beautifulNumber;
}
GroupCollection groups = regularExpression.Matches(
numberToBeautify)[0].Groups;
//More protection against invalid input
if (groups.Count < 3)
{
return beautifulNumber;
}
//Given "7689131234",
beautifulNumber += "(" + groups[1] + ")" + space; //gives us "(768) "
beautifulNumber += groups[2] + separator; //gives us "(768) 913-"
beautifulNumber += groups[3]; //gives us "(768) 913-1234"
//If we have an extension, we add it.
if (groups[4] != null)
{
beautifulNumber += space + groups[4];
}
return beautifulNumber;
}
Given inputs of:
7279884545
7279884545x12
(727) 988-4545
This returns:
(727) 988-4545
(727) 988-4545 x12
(727) 988-4545
I have a string like this:
“I’m a member of the Imperial Senate on a diplomatic mission to Alderaan.”
I want to insert <strong> around the "a" in "a diplomatic", but nowhere else.
What I have as input is diplomatic from a previous function, and I wan't to add <strong>to the closest instance of "a".
Right now, of course when I use .Replace("a", "<strong>a</strong>"), every single instance of "a" receives the <strong>-treatment, but is there any way to apply this to just to one I want?
Edit
The string and word/char ("a" in the case above) could be anything, as I'm looping through a lot of these, so the solution has to be dynamic.
var stringyourusing = "";
var letter = "";
var regex = new Regex(Regex.Escape(letter));
var newText = regex.Replace(stringyourusing , "<strong>letter</strong>", 1);
Would this suffice?
string MakeStrongBefore(string strong, string before, string s)
{
return s.Replace(strong + " " + subject, "<strong>" + strong + "</strong> " + before);
}
Used like this:
string s = “I’m a member of the Imperial Senate on a diplomatic mission to Alderaan.”;
string bolded = MakeStrongBefore("a", "diplomatic", s);
Try this:
public string BoldBeforeString(string source, string bolded,
int boldBeforePosition)
{
string beforeSelected = source.Substring(0, boldBeforePosition).TrimEnd();
int testedWordStartIndex = beforeSelected.LastIndexOf(' ') + 1;
string boldedString;
if (beforeSelected.Substring(testedWordStartIndex).Equals(bolded))
{
boldedString = source.Substring(0, testedWordStartIndex) +
"<strong>" + bolded + "</strong>" +
source.Substring(testedWordStartIndex + bolded.Length);
}
else
{
boldedString = source;
}
return boldedString;
}
string phrase = "I’m a member of the Imperial Senate on a diplomatic mission to Alderaan.";
string boldedPhrase = BoldBeforeString(phrase, "a", 41);
Hei!
I've tested this and it works:
String replaced = Regex.Replace(
"I’m a member of the Imperial Senate on a diplomatic mission to Alderaan.",
#"(a) diplomatic",
match => "<strong>" + match.Result("$1") + "</strong>");
So to make it a general function:
public static String StrongReplace(String sentence, String toStrong, String wordAfterStrong)
{
return Regex.Replace(
sentence,
#"("+Regex.Escape(toStrong)+") " + Regex.Escape(wordAfterStrong),
match => "<strong>" + match.Result("$1") + "</strong>");
}
Usage:
String sentence = "I’m a member of the Imperial Senate on a diplomatic mission to Alderaan.";
String replaced = StrongReplace(sentence, "a", "diplomatic");
edit:
considering your other comments, this is a function for placing strong tags around each word surrounding the search word:
public static String StrongReplace(String sentence, String word)
{
return Regex.Replace(
sentence,
#"(\w+) " + Regex.Escape(word) + #" (\w+)",
match => "<strong>" + match.Result("$1") + "</strong> " + word + " <strong>" + match.Result("$2") + "</strong>");
}
I have the following code that inputs a CSV file and then converts it and outputs a CSV file. I am using FileHelper for CSV reading. I am having a couple problems with it. First, an extra line with field information is being inserted (see below). The second problem is that I have added double quotes so I can have a string in a cell that contains a comma; I can't find anything wrong with the formatting but it will not import into excel and only shows the double quotes and the first item. I have tried using the double quotation mark for escaping text option when importing into excel. The third problem is that some fields are blank and result in later offset of fields.
Second line of fields:
, base, 0, 0, 0, Product Name, Size, Brand, Gender, Type, SKU, Stock, Sí, "Photo1, Photo2, Photo3", Photo1, Photo1, Photo1
Code:
using System;
using FileHelpers;
using System.IO;
[DelimitedRecord(";")]
public sealed class StoreItems
{
[FieldOptional()]
public String Attribut;
[FieldOptional()]
public String ProductName;
[FieldOptional()]
public String Size;
[FieldOptional()]
public String Brand;
[FieldOptional()]
public String Type;
[FieldOptional()]
public String ParentSKU;
[FieldOptional()]
public String SKU;
[FieldOptional()]
public String Stock;
[FieldOptional()]
public String RetailEURO;
[FieldOptional()]
public String SuggestedsellEURO;
[FieldOptional()]
public String Weight;
[FieldOptional()]
public String Description;
[FieldOptional()]
public String Photo1;
[FieldOptional()]
public String Photo2;
[FieldOptional()]
public String Photo3;
[FieldOptional()]
public String Gender;
[FieldOptional()]
public String EAN13;
}
public class RunProgram
{
public static void Main()
{
FileHelperEngine engine = new FileHelperEngine(typeof(StoreItems));
StoreItems[] res = engine.ReadFile("Presta.csv") as StoreItems[];
Directory.CreateDirectory("Output");
StreamWriter sw = new StreamWriter(#".\Output\" + DateTime.Now.ToString("hhmmss") + ".csv");
sw.AutoFlush = true;
Console.SetOut(sw);
Console.WriteLine("_type, _product_websites, tax_class_id, _visibility, is_in_stock, name, taglia, manufacturer, gender, _attribute_set, sku, qty, use_external_images, external_gallery, " +
"thumbnail_external_url, small_image_external_url, image_external_url");
foreach(StoreItems item in res)
{
string newtype = null;
int visibility = 0;
if (item.Attribut == "Parent")
{
newtype = "Configurable";
visibility = 4;
}
else if (item.Attribut == "Child")
{
newtype = "Simple";
visibility = 1;
}
int isinstock;
int stockint;
try
{
stockint = Convert.ToInt32(item.Stock);
}
catch
{
stockint = 0;
}
if (stockint > 0)
{
isinstock = 1;
}
else
{
isinstock = 0;
}
Console.WriteLine(newtype + ", base, 0, " + visibility + ", " + isinstock + ", " + item.ProductName + ", " + item.Size + ", " +
item.Brand + ", " + item.Gender + ", " + item.Type + ", " + item.SKU + ", " + item.Stock + ", Sí, " + #""""+item.Photo1+ ", "+item.Photo2+", "+item.Photo3+#"""" + ", " + item.Photo1 + ", " + item.Photo1 +
", " + item.Photo1);
}
}
}
Edit: I just realized that the second line is being added in from the reading of the input file. I just need to find a way to ignore the first line. The fields also no longer seem to be offsetting. Now just how to use commas in the csv?
Second problem;
You don't have to use commas as a separator, you can use any character you want. So as a quick fix, why not change the ',' separator in your output to '|'.
If, of course, you also need to output '|' in your text, this is not going to help, but if not - quick fix to get you going.
One problem; you have;
string newtype = null;
int visibility = 0;
if (item.Attribut == "Parent")
{
newtype = "Configurable";
visibility = 4;
}
else if (item.Attribut == "Child")
{
newtype = "Simple";
visibility = 1;
}
If item.Attribut is neither "Parent" or "Child" exactly, then newtype is not set. Hence why the first field in your example output is blank.
I’d like to create a method to replace delimiters for the intended target use (html email, log, database). The delimiters are constant so I’d like to be able to reference a object that maps recognizable names to string values (semicolon = “;”, htmlLineBreak = “<br/>”, etc.). Is there a better means to do this than this below?
public static class Utilities
{
public string ReplaceDelimiter(string content
, Delimiter currentDelimiter, Delimiter outputDelimiter)
{
return content.Replace(currentDelimiter.ToString()
, outputDelimiter.ToString());
}
}
public class Delimiter
{
public const string comma = ",";
public const string semicolon = ";";
public const string colon = ":";
public const string lineBreak = "\r\n";
public const string htmlLineBreak = "<br/>";
}
Edited following comments:
A use case would be when I want to log an error to different targets and send the same contents (formatted differently) in an email. The log may go to a database column (want key/value with semicolon delimiter) or log file (want delimiter to be line breaks). The email would be HTML so want the delimiter to be replaced with <br/>.
Below would be an excerpt from a logging method that has a few parameters including the actual Exception:
StringBuilder delimitedMessage = new StringBuilder();
delimitedMessage.Append("Date=" + DateTime.Now.ToShortDateString() + ";");
delimitedMessage.Append("Time=" + DateTime.Now.ToLongTimeString() + ";");
delimitedMessage.Append("Source=" + objException.Source.ToString().Trim() + ";");
delimitedMessage.Append("Method=" + objException.TargetSite.Name.ToString() + ";");
delimitedMessage.Append("Erring Method=" + methodName + ";");
delimitedMessage.Append("Computer=" + System.Environment.MachineName.ToString() + ";");
delimitedMessage.Append("Log Message=" + logMessage + ";");
delimitedMessage.Append("Exception Error=" + objException.Message.ToString().Trim() + ";");
delimitedMessage.Append("Severity=" + severity.ToString() + ";");
delimitedMessage.Append("Stack Trace=" + objException.StackTrace.ToString().Trim() + ";");
contentToLog = delimitedMessage.ToString();
WriteToDb(contentToLog);
WriteToLog(Utilities.ReplaceDelimiter(contentToLog, Delimiter.semicolon, Delimiter.lineBreak));
SendEmail(Utilities.ReplaceDelimiter(contentToLog, Delimiter.semicolon, Delimiter.htmlLineBreak));
Code
public class Delimiter {
public static readonly Delimiter
HtmlLineBreak=new Delimiter {
Value="<br/>"
},
LineBreak=new Delimiter {
Value="\r\n"
},
Semicolon=new Delimiter {
Value=";"
},
Colon=new Delimiter {
Value=":"
},
Comma=new Delimiter {
Value=","
};
public override String ToString() {
return Value;
}
public String Value {
get;
set;
}
}
Test
var t=Utilities.ReplaceDelimiter("123\r\n", Delimiter.LineBreak, Delimiter.HtmlLineBreak);
Debug.Print("{0}", t);
Output
123<br/>