Write a CSV file - c#

I have a class that I've written in C# and decorated with FileHelpers attributes
[DelimitedRecord(","), IgnoreFirst(1)]
class UserAndValues
{
public int UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<UserFavourites> Favourites {get;set;}
public UserAndValues()
{ }
}
class UserFavourites
{
public int Id {get;set;}
public string Title {get;set;}
}
I then call the CSV writer
var engine = new FileHelperEngine<UserAndValues>();
engine.WriteFile(myPath, myListOfUsers);
I get the following error:
The field: 'k__BackingField' has the type: ICollection`1
that is not a system type, so this field need a CustomConverter (
Please Check the docs for more Info).
I would like to write out a file that basically looks like
UserId, FirstName, LastName, Favourite1Id, Favourite1Title, Favourite2Id, Favourite2Title...
1, Joe, Smith, 1, Random Title, 2, More Title, ...
The favourites collection could be 10-50 records deep and changes based on the file.
I will not need to read this file, just write it.
How can I generate a CSV using the above class? (I can modify the class structure)

Well, very untested:
class UserAndValues
{
public int UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[FieldConverter(typeof(MyListConverter))]
public ICollection<UserFavourites> Favourites {get;set;}
}
public class MyListConverter: ConverterBase
{
public override object StringToField(string from)
{
throw new NotImplemented("bad luck");
}
public override string FieldToString(object fieldValue)
{
var list = fieldValue as ICollection<UserFavourites>;
return string.Join(",",
list.Select(f => f.ToString())); // customize
}
}

Related

Searching in json array

{
"medic":[
{
"ace":[
{
"name":"lisinopril",
"strength":"10 mg Tab",
"dose":"1 tab",
"route":"PO",
"sig":"daily",
"pillCount":"#90",
"refills":"Refill 3"
}
],
"anti":[
{
"name":"nitroglycerin",
"strength":"0.4 mg Sublingual Tab",
"dose":"1 tab",
"route":"SL",
"sig":"q15min PRN",
"pillCount":"#30",
"refills":"Refill 1"
}
],
"anticoag":[
{
"name":"warfarin sodium",
"strength":"3 mg Tab",
"dose":"1 tab",
"route":"PO",
"sig":"daily",
"pillCount":"#90",
"refills":"Refill 3"
}
],
}
]
}
class Program
{
static void Main(string[] args)
{
// ""reporttype"":""post"",
string jsonString = #"..."; //The above json
Console.WriteLine("Enter the Medication name in which you want to Find STRENGTH value :");
string medicname = Console.ReadLine();
var rootInstance = JsonConvert.DeserializeObject<Rootobject>(jsonString);
}
}
var result = rootInstance.medications[0].Where(x=>x.name == medicname ).Select(t => t.strength).ToList();
But when i run the above query, I get this below error:
'Medication' does not contain a definition for 'Where' and no accessible extension method 'Where' accepting a first argument of type 'Medication' could be found (are you missing a using directive or an assembly reference?)
I have added all necessary namespaces to my code.
and Here is my object class
public class Rootobject
{
public List<Medication> medications { get; set; }
}
public class Medication
{
public List<aceInhibitors> aceinhibitors { get ; set ; }
public List<anti> antianginal {get; set; }
public List<anticoag> anticoagulants {get; set; }
}
public class aceInhibitors
{
[JsonProperty("name")]
public string name { get; set; }
[JsonProperty("strength")]
public string strength { get; set; }
[JsonProperty("dose")]
public string dose { get; set; }
[JsonProperty("route")]
public string route { get; set; }
[JsonProperty("sig")]
public string sig { get; set; }
[JsonProperty("pillCount")]
public string pillCount { get; set; }
[JsonProperty("refills")]
public string refills { get; set; }
}
public class anti
{
public string name { get; set; }
public string strength { get; set; }
public string dose { get; set; }
public string route { get; set; }
public string sig { get; set; }
public string pillCount { get; set; }
public string refills { get; set; }
}
public class anticoag
{
public string name { get; set; }
public string strength { get; set; }
public string dose { get; set; }
public string route { get; set; }
public string sig { get; set; }
public string pillCount { get; set; }
public string refills { get; set; }
}
Your Medication object itself is not searchable. Instead it holds a bunch of list and each contains a different type (where all properties are the same). So maybe you should use some base class for the medicine and add another property to your Medication class. In that case you would have a class layout something like this:
public class Rootobject
{
public List<Medication> medications { get; set; }
}
public class Medication
{
public List<aceInhibitors> aceinhibitors { get; set; }
public List<antianginal> antianginal { get; set; }
public List<anticoagulants> anticoagulants { get; set; }
public List<betaBlocker> betablocker { get; set; }
public List<diuretic> diuretic { get; set; }
public List<Mineral> mineral { get; set; }
public IEnumerable<Medicine> Medicines => Enumerable.Empty<Medicine>()
.Concat(aceinhibitors)
.Concat(antianginal)
.Concat(anticoagulants)
.Concat(betablocker)
.Concat(diuretic)
.Concat(mineral);
}
public class Medicine
{
[JsonProperty("name")]
public string name { get; set; }
[JsonProperty("strength")]
public string strength { get; set; }
[JsonProperty("dose")]
public string dose { get; set; }
[JsonProperty("route")]
public string route { get; set; }
[JsonProperty("sig")]
public string sig { get; set; }
[JsonProperty("pillCount")]
public string pillCount { get; set; }
[JsonProperty("refills")]
public string refills { get; set; }
}
public class aceInhibitors : Medicine
{
}
public class antianginal : Medicine
{
}
public class anticoagulants : Medicine
{
}
public class betaBlocker : Medicine
{
}
public class diuretic : Medicine
{
}
public class Mineral : Medicine
{
}
And prepared with that you could now ask something like that:
var result = rootInstance.medications[0].Medicines
.Where(x => x.name == medicname)
.Select(t => t.strength)
.ToList();
If the model of the classes really matches your desires is up to you, but it should give you starting point.
If you want it more inline you could also do something like this:
public class Medication : IEnumerable<Medicine>
{
public List<aceInhibitors> aceinhibitors { get; set; }
public List<antianginal> antianginal { get; set; }
public List<anticoagulants> anticoagulants { get; set; }
public List<betaBlocker> betablocker { get; set; }
public List<diuretic> diuretic { get; set; }
public List<Mineral> mineral { get; set; }
public IEnumerator<Medicine> GetEnumerator()
{
return Enumerable.Empty<Medicine>()
.Concat(aceinhibitors)
.Concat(antianginal)
.Concat(anticoagulants)
.Concat(betablocker)
.Concat(diuretic)
.Concat(mineral)
.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
And in that case you could write something like this:
var result = rootInstance.medications[0]
.Where(x => x.name == medicname)
.Select(t => t.strength)
.ToList();
Your domain model is bit suboptimal as it was pointed out by Oliver. If you need to stick to this model, then you can do the following.
Introduce an interface for fields that are interesting from your query point of view:
public interface InterestingFields
{
string name { get; }
string strength { get; }
}
Each medication class can be easily adjusted to implement it, like:
public class Mineral: InterestingFields
{
public string name { get; set; }
public string strength { get; set; }
public string dose { get; set; }
public string route { get; set; }
public string sig { get; set; }
public string pillCount { get; set; }
public string refills { get; set; }
}
Make the properties of the Medication class queryable
var properties = typeof(Medication).GetProperties()
.Where(prop => prop.PropertyType.IsGenericType
&& prop.PropertyType.GetGenericTypeDefinition() == typeof(List<>)
&& typeof(InterestingFields).IsAssignableFrom(prop.PropertyType.GetGenericArguments()[0]))
.ToList();
I've used reflection where the property's type is a List<T> and T is assignable to InterestingFields
Go through the properties, retrieve the actual value and do the filtering based on that
var medication = rootInstance.medications[0];
var result = from property in properties
let collection = property.GetValue(medication) as IEnumerable<InterestingFields>
let element = collection?.ToArray().First()
where element?.name == medicname
select element.strength;
Console.WriteLine(result.First());
Proper design would lead to a separation from the data handling and the way that your data is stored. This way, it is easy to reuse the stored data for other handling, it is easier to unit test the data handling with test code, you can change the way that the data is stored, to for instance a CSV file, or XML, without having to change the data handling code.
So you need a class Medication:
class Medication
{
public string Name {get; set;}
public string Strength {get; set;}
public string Dose {get; set;}
... // etc.
}
Consider to change Dose and Strength to a numerical value.
Apparently you have stored all Medications somewhere. A proper software design would hide where it is stored, and what format it is stored in. All you know is, that you can store Medications in it, and fetch it back later, even after your program is restarted. Such a storage is often called a Repository:
class MedicationRepository
{
public IEnumerable<Medication> ReadMedications() {...}
}
The actual implementation is up to you. I think you'll use Nuget Package NewtonSoft Json for this. Maybe you also want methods to Add / Change / Remove Medications?
Consider to let the Repository class implement IEnumerable<Medication>, or even ICollection<Medication>, depending on what is most efficient in your case.
class MedicationRepository : IEnumerable<Medication>
{
public IEnumerator<Medication> GetEnumerator()
{
return this.ReadMedications().GetEnumerator();
}
...
}
Now that you've got a method to read all Medications, we can get back to your LINQ problem:
I need get input string from user(which is medication name in json) i need to check if input matches the name in medication and need to display corresponding strength value.
So you've got a procedure to read the medication name:
public string ReadMedicationName() {...}
And you want the Strength of all Medications with this name.
MedicationRepository medications = ...
string requestedMedicationName = this.ReadMedicationName();
string medicationStrength = medications
.Where(medication => medication.Name == requestedMedicationName)
.Select(medication => medication.Strength)
.FirstOrDefault();
In words: from all Medications, keep only those Medications that have a name that equals requestedMedicationName. If the name is unique, then there will be zero or one Medication left. From all remaining Medications, take only the value of property Strength, and take the first strength, or null if there is no Medication with this Name at all.
Can it be that there are several Medications with this name? Which one do you want in that case, just any Strength (= .FirstOrDefault()), all Strengths (= ToList())? In the latter case: how do you distinguish which Medication with this name contains which Strength? Consider to Select more properties in that case.
Conclusion
By separating the storage of the data and how you get the requested Medication Name from the data handling, it is easier to change the storage (to XML, to CSV, to a database), and it is easier to unit test the LINQ using specific test data.
Similarly: you've hidden how you get the name of the requested Medication: is it a DOS prompt? Did you read it from a file? Maybe you've changed it to a WinForms application and you read it from a Textbox, or a ComboBox. Because you separated, the LINQ doesn't have to change, and can be reused in several platforms.

JSON Class with spaces in name

I have been given a JSON definition something along the lines of..
{
"the.data" : {
"first.name": "Joe",
"last.name": "Smith"
}
}
and i've made a class in c# to add my data too, such as
public class TheData
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class RootObject
{
public TheData TheData { get; set; }
}
Now the web service that i have to send this payload to, is expecting The.Data, and First.Name, as well as Last.Name
How can i 'change' the name definitions? Before transmitting?
Can i somehow override the name?
You can try this. you can use JsonPropertyAttribute to tell Json.Net what the property's corresponding json field is.
public class TheData
{
[JsonProperty("first.name")]
public string FirstName { get; set; }
[JsonProperty("last.name")]
public string LastName { get; set; }
}
public class RootObject
{
[JsonProperty("the.data")]
public TheData TheData { get; set; }
}
You can decorate the values, it will depend on which framework you're using but it'll be something like this:
[JsonProperty(PropertyName = "Person.Name")]
public string PersonName { get; set; }

Readonly nested object properties

I'm having a problem defining these 2 classes:
public class Article
{
public Article(long ID, string Name, ArticleFamily Family)
{
//...Initializer...
}
public ArticleFamily Family { get; set; }
//Other props...
}
public class ArticleFamily
{
public ArticleFamily(int ID, string Description)
{
//...Initializer...
}
public int ID { get; private set; }
public string Description { get; set; }
}
I have a collection of Article and each one belongs to a family.
Now, given that I have a certain ArticleFamily object I should be able to change its Description and it gets eventually persisted to a DataBase. (I left out that part for simplicity)
But I should not be able to do this:
Article art = SomeMethodReturningArticle();
art.Family.Description = "SomeOtherValue";
I should be able to change the Family of an Article entirely, replacing it with a new ArticleFamily object, but I shouldn't be able to change just the description.
Should I create a copy of the ArticleFamily class with readonly properties like this:
public class ArticleFamilyReadonly
{
ArticleFamily _family;
public ArticleFamilyReadonly(ArticleFamily Family)
{
_family = Family;
}
public int ID { get { return _family.ID; } }
//etc...
}
How can I do this in a clean way?
Here's what I threw together in LinqPad:
void Main()
{
var art = new Article(1,"2", new ArticleFamily(1, "Test"));
art.Family.Description = "What?"; // Won't work
var fam = art.Family as ArticleFamily;
fam.Description = "This works"; // This works...
}
public class Article
{
public Article(long ID, string Name, IArticleFamily Family)
{
//...Initializer...
}
public IArticleFamily Family { get; set; }
//Other props...
}
public class ArticleFamily : IArticleFamily
{
public ArticleFamily(int ID, string Description)
{
//...Initializer...
}
public int ID { get; private set; }
public string Description { get; set; }
}
public interface IArticleFamily
{
int ID { get; }
string Description { get;}
}
Cannot edit directly from the Article object unless cast to ArticleFamily object.

Class with list of class within

I´m new to the OO-Stuff. What I want is follwing:
A class, that contains some properties and additional a list of classes. Here a pseudo code:
[User]
[Firsname]
[Lastname]
["List of Photos"]
[Photo-1]
[Photo-2]
[Photo-n]
I have a class "User" defined (wihtout my "list of photos"):
public class user
{
string Firstname { get; set; }
string Lastname { get; set; }
}
And I have a class "Photos":
public class photos
{
string filename { get; set; }
datetime timestamp { get; set; }
}
How can I put "photos" into user, so that I can add and retrieve n photos from a user?
Use List:
public class user
{
string Firstname { get; set; }
string Lastname { get; set; }
List<photos> photos{get;set;}
}
Note: photos is not good for naming please rename photos to photo
for use:
var u=new user();
u.photos=new List<photos>();
u.photos.Add(//add photo)
Also you can use:
public class user
{
string Firstname { get; set; }
string Lastname { get; set; }
List<photos> photos{get;set;};
public User()
{
photos= new List<photos>();
}
}
and for use:
var u=new user();
u.photos.Add(//add photo)
Also for naming you can use this.
Collection properties should almost universally be read-only. As such, your classes should look like this:
public class Photo
{
public string FileName { get; set; }
public DateTime TimeStamp { get; set; }
}
public class User
{
private readonly List<Photo> _photos = new List<Photo>();
public string GivenName { get; set; }
public string FamilyName { get; set; }
public List<Photo> Photos
{
get
{
return this._photos;
}
}
}
If you want lazy-loading then the User class should look like this:
public class User
{
private List<Photo> _photos;
public string GivenName { get; set; }
public string FamilyName { get; set; }
public List<Photo> Photos
{
get
{
return this._photos ?? (this._photos = new List<Photo>());
}
}
}
You could also do away with the field and use a private setter:
public class User
{
public string GivenName { get; set; }
public string FamilyName { get; set; }
public List<Photo> Photos { get; private set; }
public User()
{
this.Photos = new List<Photo>();
}
}
In each case, code outside the User class cannot assign directly to the Photos property, meaning that the existing collection cannot be replaced. You can only get the collection from the property and Add, Remove or whatever. That's how collection properties work throughout the Framework, e.g. DataSet.Tables, DataTable.Columns, DataTable.Rows, Control.Controls, ComboBox.Items, ListView.Items, etc, etc, and the list goes on.

appending a collections object

I am working on an existing application that is a package tracking application and I want to append a collections list object.
My code is:
public class VehicleAssignElement
{
public VehicleAssignElement(string pkgID, string DriverID, string VehicleID)
{
pkgID = ConfID;
DriverID = puID;
VehicleID = doID;
}
public string pkgID { get; set; }
public string DriverID { get; set; }
public string VehicleID { get; set; }
}
public class VehicleAssignID
{
public List<VehicleAssignElement> AssignID { get; set; }
}
public class VehicleAssignIDList
{
public List<VehicleAssignID> AssignRecord { get; set; }
}
I have a code block where I loop through to get the pkgID.
assignElement.AssignID.Add( new VehicleAssignElement(oPackageTracking.pkgID,"",""));
I have another code block that I need to loop through to get the DriverID and a 3rd code block to loop the get the VehicleID.
Question:
How would I go about appending the collection to add DriverID as the second element and the VehicleID as the 3rd element?
Using assignElement.AssignID.Add but that adds to the collection. I tried Insert, but was unsuccessful as well.
Any advice is appreciated.
I think you meant something like this:
public class VehicleAssignElement
{
public VehicleAssignElement(string pkgID, string driverID, string vehicleID)
{
this.PkgID = pkgID;
this.DriverID = driverID;
this.VehicleID = vehicleID;
}
public string PkgID { get; set; }
public string DriverID { get; set; }
public string VehicleID { get; set; }
}
public class Form1 : Form
{
private List<VehicleAssignElement> _assignmentList = new List<VehicleAssignElement>();
public void DoSomething()
{
VehicleAssignElement assignment1 = new VehicleAssignElement("pkg1", "John", "Audi");
_assignmentList.Add(assignment1);
VehicleAssignElement assignment2 = new VehicleAssignElement("pkg2", "Morgan", "Volkswagen");
_assignmentList.Add(assignment2);
foreach(var assignment in _assignmentList)
{
Debug.WriteLine(string.Format("{0} -> {1} - {2}", assignment.PkgID, assignment.DriverID, assignment.VehicleID));
}
}
}
NOT TESTED. So it may contain some syntax errors.. But this is for example.

Categories

Resources