Related
Hi I am using this function to show json in DataGridView but the problem is I have a list in the json file , this list doesn't display data in the grid
void show_data()
{
dataGrid.Columns.Clear();
dataGrid.DataSource = all_date;
}
my json file is as this
[{"Name":"350SC250-14","Fy":33.0,"GFy":false,"T":0.0713,"GT":false,"D":2.5,"Stud_D":1.5,"C":0.0,"B":3.5,"GB":false,"Manufacturer":"BIDDLE","CFS_File":"350SC250-14.cfss","Sub_SectionCount":0,"Sub_Section1":"","Sub_Section2":"","Sub_Section3":"","SectionType":3,"Configuration":0,"SectionParts":[{"Length":0.375,"Radius":0.1069},{"Length":0.5,"Radius":0.1069},{"Length":3.0,"Radius":0.1069},{"Length":0.5,"Radius":0.1069},{"Length":0.5,"Radius":0.1069},{"Length":2.5,"Radius":0.1069},{"Length":0.5,"Radius":0.1069},{"Length":0.5,"Radius":0.1069},{"Length":3.0,"Radius":0.1069},{"Length":0.5,"Radius":0.1069},{"Length":0.375,"Radius":0.0}]}]
my class is bellow
class dataForm
{
public string Name { get; set; }
public double Fy { get; set; }
public bool GFy { get; set; }
public double T { get; set; }
public bool GT { get; set; }
public double D { get; set; }
public double Stud_D { get; set; }
public double C { get; set; }
public double B { get; set; }
public bool GB { get; set; }
public string Manufacturer { get; set; }
public string CFS_File { get; set; }
public int Sub_SectionCount { get; set; }
public string Sub_Section1 { get; set; }
public string Sub_Section2 { get; set; }
public string Sub_Section3 { get; set; }
public int SectionType { get; set; }
public int Configuration { get; set; }
public List<SectionPart> SectionParts { get; set; }
}
class SectionPart
{
public double Length { get; set; }
public double Radius { get; set; }
}
so how can I diplay this sectionPart list ?
Add Newtonsoft.Json nuget :Install-Package Newtonsoft.Json to your project, and convert JSON to object using DeserializeObject
string data_from_json = #"[{'Name':'350SC250 - 14','Fy':33.0,'GFy':false,'T':0.0713,'GT':false,'D':2.5,'Stud_D':1.5,'C':0.0,'B':3.5,'GB':false,'Manufacturer':'BIDDLE','CFS_File':'350SC250 - 14.cfss','Sub_SectionCount':0,'Sub_Section1':'','Sub_Section2':'','Sub_Section3':'','SectionType':3,'Configuration':0,'SectionParts':[{'Length':0.375,'Radius':0.1069},{'Length':0.5,'Radius':0.1069},{'Length':3.0,'Radius':0.1069},{'Length':0.5,'Radius':0.1069},{'Length':0.5,'Radius':0.1069},{'Length':2.5,'Radius':0.1069},{'Length':0.5,'Radius':0.1069},{'Length':0.5,'Radius':0.1069},{'Length':3.0,'Radius':0.1069},{'Length':0.5,'Radius':0.1069},{'Length':0.375,'Radius':0.0}]}]";
dataGrid.Columns.Clear();
List<dataForm> all_date = JsonConvert.DeserializeObject<List<dataForm>>(data_from_json);
dataGrid.DataSource = all_date;
to display SectionParts only:
dataGrid.DataSource = all_date[0].SectionParts;
The problem you describe does not really have anything to do with JSON. The problem is with the Class dataForm. I do not like to say “problem” since the issue is fairly easy to understand, once you understand what your code is asking from the grid. The issue is that when the grid is given a DataSource like a List<T> or List<dataForm>, the grid will obligingly map each “primitive” property of the dataForm class (or any class T) to a column in the grid.
The grid is going to have to do something different when it comes across a property in the class that is a “Collection” or another “Class”. In this case, dataForm has a “collection” property …
public List<SectionPart> SectionParts { get; set; }
In this case the grid is not sophisticated enough to figure this out. It really is not going to know how to put “multiple” values into a single cell. Therefore, the grid will ignore collections as you obviously already know. This same Idea applies if the property in the class is another class… it will not display it.
Bear in mind, as other have commented, there are third-party controls that may have features that will show the collections or classes. Unfortunately, the out of the box DataGridView is fairly limited. In most cases, the easiest way to deal with this is in a “Master/Detail” type display, where the first grid displays the class and the second grid displays the collection of the “selected” item in the first grid.
With that said, there are ways to achieve your goal without a lot of work depending on “how” you want to “display” the data to the user. IMHO a master-detail is usually the easiest to implement and is user friendly. Another option, is to “flatten” each of the items in the collection creating a column(s) for each item in the collection. This is much more work and you will possibly end up with many columns and many empty cells… not user friendly. The last option which may work for your case is to create a public property in the dataForm class that “exposes” the SectionParts collection as a single string.
Example: in this case, the SectionPart class has two (2) properties.
public double Length { get; set; }
public double Radius { get; set; }
To help later, let’s override the ToString method to return the two values as a single string. Something like…
public override string ToString() {
return "L: " + Length + " R: " + Radius;
}
Moving to the dataForm class there is a “collection” of the SectionPart objects. To display ALL the section parts into a single cell we need to create a single string from ALL the section parts. Each part will be on a single line using the SectionParts ToString method we added above. To make this “single” string display in the grid, we need to add a new property to the dataForm class that returns this “list of parts” as a single string. This property may look like…
public string SectionPartsList {
get {
StringBuilder sb = new StringBuilder();
foreach (SectionPart sp in SectionParts) {
sb.AppendLine(sp.ToString());
}
return sb.ToString();
}
}.
You may need to adjust the grids rows height to accommodate, however, in my test, if you simply hover your cursor over the cell the list will display.
Lastly, for completeness, let us assume, that instead of a “List” of SectionParts the class had a single instance of this class.
public SectionPart SingleSectionPart { get; set; }
Again the grid is going to ignore this “class” property when creating the columns in the grid. However, if you wanted those two fields displayed in the grid, then it would be fairly simple to create two properties in the dataForm class that simply return those values. In this case, the two properties may look like…
public double SP_L {
get {
return SingleSectionPart.Length;
}
}
public double SP_R {
get {
return SingleSectionPart.Radius;
}
}
I hope this makes sense and helps.
This question already has answers here:
How to auto-generate a C# class file from a JSON string [closed]
(3 answers)
Closed 3 years ago.
I am attempting to deserialize a Json object that was returned from a web API with the structure as follows.
Here is my class structure in which the object is to be Deserialized into...
public class CandidateJson
{
public string response { get; set; }
//public string result { get; set; }
public Result result { get; set; }
public string Candidates { get; set; }
public List<row> rows { get; set; }
}
public class Result
{
public string result { get; set; }
public string uri { get; set; }
}
public class row
{
public string no { get; set; }
public List<FL> FL { get; set; }
}
public class FL
{
public string val { get; set; }
public string content { get; set; }
}
I am using the following line of code to Deserialized with no success....
var json = JsonConvert.DeserializeObject<CandidateJson>(JsonResult);
Upon execution of this line of code, I am prompted with the following error...
Unexpected character encountered while parsing value: {. Path 'response', line 1, position 13.
I would appreciate any assistance with this issue.
Please let me know if any additional information is needed.
Here is the raw JSON string:
{"response":{"result":{"Candidates":{"row":[
{"no":"1","FL":[{"val":"CANDIDATEID","content":"508304000012555617"},{"val":"Candidate ID","content":"ZR_129661_CAND"},{"val":"First Name","content":"PRODUCTION"},{"val":"Last Name","content":"TEST"},{"val":"Email","content":"patricia.conley#ampcorporate.com"},{"val":"Phone","content":"815-543-2109"},{"val":"Mobile","content":"815-555-5555"},{"val":"Street","content":"555 Test Ave"},{"val":"City","content":"DeKalb"},{"val":"State","content":"IL"},{"val":"Zip Code","content":"60115"},{"val":"SMCREATORID","content":"508304000000490014"},{"val":"Created By","content":"AMP Support IT Team"},{"val":"MODIFIEDBY","content":"508304000000227003"},{"val":"Modified By","content":"Nikki Bowman"},{"val":"Created Time","content":"2019-12-17 08:38:25"},{"val":"Updated On","content":"2019-12-20 15:23:10"},{"val":"Last Activity Time","content":"2019-12-20 15:23:10"},{"val":"SMOWNERID","content":"508304000000490014"},{"val":"Candidate Owner","content":"AMP Support IT Team"},{"val":"Source","content":"Non-Employee Referral"},{"val":"Email Opt Out","content":"false"},{"val":"Is Locked","content":"false"},{"val":"Is Unqualified","content":"false"},{"val":"Is Attachment Present","content":"false"},{"val":"Candidate Status","content":"Sales Training Scheduled"},{"val":"Career Page Invite Status","content":"0"},{"val":"Extension","content":"5555"},{"val":"Sales Training Date_ID","content":"508304000011808848"},{"val":"Sales Training Date","content":"2019-12-11 Digital Sales Training"},{"val":"Start Date","content":"2019-12-17"},{"val":"Candidate Job Category","content":"Print + Digital Outside"},{"val":"District Sales Manager","content":"Luke Wasowski"},{"val":"College Graduate","content":"false"},{"val":"Recruiter Initials","content":"NKB"},{"val":"Unit/Apt/Ste","content":"Apt 5"},{"val":"Hourly Rate","content":"5.00"},{"val":"Work State","content":"Illinois"},{"val":"Full Time/Part Time","content":"FTR"},{"val":"Work Email Address","content":"Nikki.Bowman#ampcorporate.com"},{"val":"EEO Class","content":"1.1"}]},
{"no":"2","FL":[{"val":"CANDIDATEID","content":"508304000011834365"},{"val":"Candidate ID","content":"ZR_125018_CAND"},{"val":"First Name","content":"Jennifer"},{"val":"Last Name","content":"Pedersen"},{"val":"Email","content":"jennyped248_hwo#indeedemail.com"},{"val":"Mobile","content":"+18157517187"},{"val":"City","content":"Genoa"},{"val":"State","content":"IL"},{"val":"Zip Code","content":"60135"},{"val":"Country","content":"United States"},{"val":"Experience in Years","content":"8"},{"val":"Current Employer","content":"WALMART"},{"val":"Current Job Title","content":"MOD TEAM MEMBER"},{"val":"Skill Set","content":"quick and exceptional customer experience, Helping and Advising Customers, Basic Word Processing, Communication Skills, Customer Service, Data Entry, Hard-Working, Intermediate Word Processing, Organisational Skills, Teamwork, Time Management, outstanding communication skills, Microsoft Word, Microsoft Excel, Microsoft Excel 2000, Microsoft Office, Microsoft Outlook, Microsoft PowerPoint, basic scheduling"},{"val":"SMCREATORID","content":"508304000000562001"},{"val":"Created By","content":"Matt Chenoweth"},{"val":"MODIFIEDBY","content":"508304000008810064"},{"val":"Modified By","content":"HR Department"},{"val":"Created Time","content":"2019-12-02 12:25:53"},{"val":"Updated On","content":"2019-12-12 09:04:51"},{"val":"Last Activity Time","content":"2019-12-12 09:04:51"},{"val":"SMOWNERID","content":"508304000000562001"},{"val":"Candidate Owner","content":"Matt Chenoweth"},{"val":"Source","content":"Indeed Resume"},{"val":"Email Opt Out","content":"false"},{"val":"Is Locked","content":"false"},{"val":"Is Unqualified","content":"false"},{"val":"Is Attachment Present","content":"true"},{"val":"Candidate Status","content":"Hired - AMP Office"},{"val":"Career Page Invite Status","content":"0"},{"val":"Source By","content":"Applied by Candidate"},{"val":"EMPID","content":"JFP147"},{"val":"Candidate Job Category","content":"Office - Digital Verification"},{"val":"College Graduate","content":"false"}]
}]}}
,"uri":"/recruit/private/json/Candidates/searchRecords"}}
I haven't tested it, but by the looks of it, your code should look like:
public class CandidateJson
{
public Response response { get; set; }
}
public class Response
{
public Result result { get; set; }
public string uri { get; set; }
}
public class Result
{
public Candidate Candidates { get; set; }
}
public class Candidate
{
public List<Row> row { get; set; }
}
public class Row
{
public string no { get; set; }
public List<FL> FL { get; set; }
}
public class FL
{
public string val { get; set; }
public string content { get; set; }
}
Note: You might want to use int or decimal instead of string for val and no, but there is not enough information for me to assert that.
My application's front end is a DataGrid whose contents are passed to an Excel-generating method.
The catch is that the DataGrid deals with 13 columns: 10 of them are fixed (i.e., passed to Excel) while each of the last 3 is optional.
public class GridModel
{
public string Item { get; set; }
public string ItemName { get; set; }
public double TotalHeight { get; set; }
public double AGLheight { get; set; }
public double Embedment { get; set; }
public string Shape { get; set; }
public double TipDiameter { get; set; }
public double BaseDiameter { get; set; }
public double Taper { get; set; }
public double ShearReaction { get; set; }
// The following are optional, in 8 combinations, from all present to all absent
public double Camber { get; set; }
public double Rake { get; set; }
public double Angle { get; set; }
}
Being a C# newbie, I am considering the different approaches.
How would you folks deal with this? The simplest idea that comes to mind is to add 3 flags to the model:
bool IsColumn1Present;
bool IsColumn2Present;
bool IsColumn3Present;
Another way would be to add a level to the hierarchy, so each of the 'special' columns contains its own embedded flag:
if (Camber.flag) add(Camber.value);
That said, I would like to have the ability to somehow remove those 3 properties, so any attempt to access them would result in an error or impossibility.
If such thing exists, I guess it would be called "Variant Properties".
TIA
Note: I have solved this already by the manipulation of the Visibility.Visible field at the GUI level. Gurus, however, tell us that this is a bad idea. Best practices dictate that this facility should be part of the Model.
You could use nullable properties:
public double? Camber { get; set; }
Then check them for a value in your business logic:
if (thing.Camber.HasValue)
{
DoSomething(thing.Camber.Value);
}
It sounds like this might be exactly what you're after, given your comment on "variant" properties.
More info: http://msdn.microsoft.com/en-us/library/1t3y8s4s.aspx
Update: If you need to switch them off application-wide (as per your comment), you could either avoid setting the value in the first place when it's not wanted (this would be preferable as this is, as far as I'm concerned, business logic and doesn't belong in your dumb model classes) or extend this with a custom accessor:
private double? _camber;
public double? Camber
{
get
{
return ModelSettings.CamberEnabled
? _camber
: null;
}
set;
}
Then have some static/constant property somewhere:
public static class ModelSettings
{
public const bool CamberEnabled = true;
}
If the number of columns is constant (meaning the user can't add 'custom' columns), I would suggest a bit-field enum value like so:
[Flags]
public enum ColumnFlags
{
None = 0,
Camber = 0x1,
Rake = 0x2,
Angle = 0x4,
// Other optional columns here, keep them powers of 2!
}
Then in your Model class, keep a value such as:
public ColumnFlags ColumnFlags { get; set; }
Then you can use...
if(model.ColumnFlags.HasFlag(ColumnFlags.Camber))
{
// Do something here...
}
if(model.ColumnFlags.HasFlag(ColumnFlags.Rake))
{
// Do something here...
}
EDIT: Alternatively, you can use the Nullable<T> types to specify a "missing" or "empty" value.
Over the past two years I developed apps for the CF .NET 3.5 to be runned on warehouse's portable device(windows mobile).
From the beginning I just jumped into the process and made a lot of mistakes that I'm gradually correcting. What has came out are apps made in this way:
a main form to start the whole process which automatically creates a data-form, that will stay alive for the whole time. This data-form will keep all the datas that the user will insert or request from the server. The other forms are basically views of the data with methods to manipulate them.
It works but...am I doing this in the right way? Or maybe am I missing something really fundamental?
So, you created a data form, and you are using it like RAM. You never display the data, you simply store it there to access.
If someone ever has to take over your job (like you leave the company or die), they are going to hate you so bad.
A better technique would be to create a Class that houses all of this data.
The good part is, since you already have a data form, you probably already know how everything is organized!
Now, just use that knowledge of your data to create your class that you can read and write to.
If you have groups of similar items, create other classes that your main class will contain.
If you have several of these similar items, create publically accessible Lists of these items.
Make it as dead simple or as complex as you'd like!
Consider these classes, which are all generic enough to modify however you would need and demonstrate some extras added:
public class DataForm {
private GroupedItem m_item2;
public event EventHandler Item2Changed;
public DataForm() { // this is your constructor
Item1 = new GroupedItem();
Item2 = new GroupedItem();
ItemCollection = new GroupCollectionItems("Group1");
}
public float Value1 { get; set; }
public float Value2 { get; set; }
public GroupedItem Item1 { get; set; }
public GroupedItem Item2 {
get { return m_item2; }
set {
if (m_item2 != value) {
m_item2 = value;
if (Item2Changed != null) {
Item2Changed(this, EventArgs.Empty); // notify whoever is listening for the change
}
}
}
}
public GroupCollectionItems ItemCollection { get; set; }
}
public class GroupedItem {
public GroupedItem() { // this is your constructor
}
public string Name { get; set; }
public object Value { get; set; }
}
public class GroupCollectionItem {
private GroupCollectionItem() { // this is your constructor
}
public static GroupCollectionItem Create(string groupName, string itemName, object itemValue) {
var item = new GroupCollectionItem() {
Group = groupName,
Name = itemName,
Value = itemValue
};
return item;
}
public string Group { get; private set; }
public string Name { get; private set; }
public object Value { get; set; }
}
public class GroupCollectionItems : List<GroupCollectionItem> {
public GroupCollectionItems(string name) { // this is your constructor
Name = name;
}
public string Name { get; private set; }
}
In my application I download some data from website providing landscape informations like population, landscape area and few more.
After parsing html code, data are stored in list of objects
public class Landscape
{
public int Population { get; set; }
public string Area { get; set; }
}
public List<Landscape> data;
No problem with that. Follows filling dataGrid and that's purpose of my application. Now, where is the problem? Area info on the website is in semi-numeric, semi-string format. For example: 10 000 km2.
I have possibility to choose whether to trim km2 suffix and store data as int, or do nothing about it and store data as string. Since I want have data in dataGrid in original format, I decided not to trim.
Finally, is there a way to order rows (by size of area) as int although it is string type?
You can use linq to sort by the translated value but don't think this will be fast.
public class Landscape
{
public int Population { get; set; }
public string Area { get; set; }
public int TrimAndConvert()
{
/* Your Code to trim and convert to int */
}
}
data.OrderBy(landscape => landscape.Area.TrimAndConvert());
I think a better way would be to save the Area as a number and add the trimmed part just for output.
public class Landscape
{
public int Population { get; set; }
public int AreaValue { get; set; }
public string AreaUnit { get; set; }
public string Area
{
get
{
return AreaValue.ToString() + AreaUnit;
}
}
}
You will need to implement an IComparer and pass and instance of it to the List.Sort() method/overload that takes an IComparer.
This should get you started:
List<T>.Sort Method (IComparer<T>)