Populate List with Json file - c#

I have a List of Items. Item is an object with multiple constructors, thus an item can be created in several forms.
Example of class Item with two constructors.
public class Item
{
public string name = "";
public int age= 0;
public int anotherNumber = 0;
public Item(string iName, int iAge)
{
name = iName;
age= iAge;
}
public Item(string iName, int iAge, int iAnotherNumber)
{
name = iName;
age= iAge;
}
}
I have then a Json file in the form of:-
[{"name":"Joseph","age":25},{"name":"Peter","age":50}]
I'm using the below method to read file and populate list using Newtonsoft.Json API.
public List<Item> ReadJsonString()
{
List<Item> data = new List<Item>();
string json = File.ReadAllText("\\path");
data = JsonConvert.DeserializeObject<List<Item>>(json);
return data;
}
If I have only one constuctor in the Item class (example the constructor that takes 2 arguments) this method works fine and I am able to populate the list. However when I add the second constructor in class Items(since I want to be able also to read Json files with the third attribute..
Example:-
[{"name":"Joseph","age":25, "anotherNumber": 12},{"name":"Peter","age":50, "anotherNumber": 12}]
The ReadJsonObj method fails with error "Unable to find a constructor to use for type Item". I can fix this issue by creating multiple class Item (e.g. ItemA, ItemB),one that takes three variables and the other that takes two variables. However I would like to have only one Item class.
I cannot find out why this is happening and how to fix such an issue.

You can use properties instead of regular fields and constructor initialization.
public class Item
{
public string name {get;set;}
public int age {get;set;}
public int anotherNumber {get;set;}
}
Thus you will cover both deserialization cases.

Related

Accessing a List of Class objects

I'm trying to get the following to work:
A class called Caption, where I populate a List - adding Items to the Class list
I then want to reference the list of class values, in a lookup method
public class Caption
{
readonly CaptionKey _CaptionKey; //Enum list
readonly string _Description;
public Caption(CaptionKey captionKey, string description)
{
_CaptionKey = captionKey;
_Description = description;
}
public CaptionKey CaptionKey { get { return _CaptionKey; } }
public string Description { get { return _Description; } }
}
Here is the class that creates the class list
public class InitCaptions
public static List<Caption> _Captions = new List<Caption>();
// the class access I need
public static string LookupCaption( CaptionKey )
{
//? How to return the description for
}
The problem is with referencing the list of classes from another class and process.
I can see the values in the debugger are there-
System.Collections.Generic.List<MyNamespace.Controllers.Captions>
I'm just not sure how to reference it properly.
I should add - this is a MVC solution - so the List is created in the application startup, but the reference call is done from a report - using ReportViewer. the List shows in the code using Intellisense, but when run - the List is not there.
Assuming a Simple Dictionary<> won't suit your needs (for reasons you have not specified),
then to find and return an item from a List you can use the List.Find method, which takes a delegate and is used like this:
Caption foundItem = _Captions.Find(delegate (Caption obj) { return obj.Description.equals("Find this Description"); });
If there is more than one, then it will return the first it finds.
There is also the FindAll(....) version of this which will return a new list of all matches items.

How i can get object in class List

I created my class
class TxtEmail
{
public TxtEmail(string firtFirstmail, string domain)
{
this.Firstmail = firtFirstmail;
this.Domain = domain;
}
public string Firstmail { get; set; }
public string Domain { get; set; }
public string RetOneString()
{
return Firstmail + "#" + Domain;
}
}
Then my class add to List Class
class EmailDP : List<TxtEmail>
{
List<TxtEmail> txtemail = new List<TxtEmail>();
public void Add(string path)
{
txtemail.Add(new TxtEmail("user1", "google.ru"));
txtemail.Add(new TxtEmail("user5555", "google.com"));
txtemail.Add(new TxtEmail("user252", "outlook.com"));
txtemail.Add(new TxtEmail("user3", "gmail.com"));
}
another methods ......
But then i created object my classes, he show 0 Count, Why? Where i make mistake and how i can get object in it?
EmailDP em1 = new EmailDP();
MessageBox.Show(em1.Count.ToString()); -> this show 0
foreach (var myob in em1)
{
MessageBox.Show(myob.RetOneString());
}
You have two lists involved:
The class EmailDP itself derives from List<TxtEmail> and is therefore a list whose count you are displaying
The internal list txtemail to which you actually add the items, leaving EmailDP itself empty.
Change your class, so that it encapsulates the inner list.
class EmailDP
{
private readonly List<TxtEmail> _txtemail = new List<TxtEmail>();
public void Add(string path)
{
_txtemail.Add(new TxtEmail("user1", "google.ru"));
_txtemail.Add(new TxtEmail("user5555", "google.com"));
_txtemail.Add(new TxtEmail("user252", "outlook.com"));
_txtemail.Add(new TxtEmail("user3", "gmail.com"));
}
public int Count => _txtemail.Count;
public IEnumerable<TxtEmail> EmailTexts => _txtemail;
// ... other methods
}
And of course you have to call Add at least once...
EmailDP em1 = new EmailDP();
em1.Add("some path");
em1.Add("another path");
MessageBox.Show(em1.Count.ToString());
foreach (var myob in em1.EmailTexts)
{
MessageBox.Show(myob.RetOneString());
}
If you override ToString instead of creating a method RetOneString, you have the advantage that the TxtEmail objects will be displayed automatically at several places. I.e. in the debugger, in listboxes or in Console.WriteLine.
public override string ToString()
{
return Firstmail + "#" + Domain;
}
Your EmailDP is-a List and also has-a List and I don't think you mean for both of those to be true. It's a question of inheritance vs composition. Do you need your class to be a list or can it contain a list instead?
If you want your class to be-a List, then in your add method, you can do:
this.Add(new TxtEmail("user1", "google.ru")); ...
If you want your class to contain-a List then you can remove the inheritance of List
class EmailDP
{
...
And then make your list public so it is accessible:
public List<TxtEmail> txtemail = new List<TxtEmail>();
And then get the count from the list contained within:
MessageBox.Show(em1.textemail.Count.ToString());
Hope that helps.
You need to call the Add Method which fills your array.
Also do not inherit the List as you do not need this.
EmailDP em1 = new EmailDP();
em1.Add(string.Empty);
MessageBox.Show(em1.Count.ToString()); -> You will get items you added in the Add Method
foreach (var myob in em1)
{
MessageBox.Show(myob.RetOneString());
}

Assign values to dynamic number of sub-classes before serializing to JSON

I am integrating with a courier that requires me to pass box dimensions for each box in my consignment to their API in JSON format. I am able to set individual properties like RecipientName, but am not sure how to pass the box details for the varying number of boxes for each consignment.
The JSON needs to look like this (example is for a 2 box consignment):
{
"RecipientName": "Joe Bloggs",
"Packages" : [{
"boxNumber": "1",
"boxHeight": 1.55,
"boxLength": 1.55,
"boxWidth": 1.55
},
{
"boxNumber": "2",
"boxHeight": 2.55,
"boxLength": 2.55,
"boxWidth": 2.55
}]
}
I have built 2 classes, one that describes the structure of the JSON, and another that contains the method to serialize the JSON.
My JSON structure class looks like this (I have used a List because I have read that arrays are a fixed length, and because the number of boxes with vary I cannot use arrays):
public class API_JSON
{
public class Rootobject
{
public string RecipientName { get; set; }
public List<Package> Packages { get; set; }
}
public class Package
{
public string boxNumber { get; set; }
public double boxHeight { get; set; }
public double boxLength { get; set; }
public double boxWidth { get; set; }
}
}
And my API methods class looks like this:
public class API_Methods
{
public string recipientName;
public List<string> boxnumber;
public List<double> boxHeight;
public List<double> boxLength;
public List<double> boxWidth;
public Boolean SubmitConsignment(out string JSONData)
{
var NewRequestObject = new API_JSON.RootObject
{
Recipient = recipientName,
Packages = new API_JSON.Package
{
foreach (string item in ContainerNumber)
{
boxNumber=???,
boxHeight=???,
boxLength???=,
boxWidth=???
}
}
}
string JSONData = JsonConvert.SerializeObject(NewRequestObject);
return true;
}
}
I am then instantiating the object, setting its public variables, then running the method list this:
API_Methods myObject = new API_Methods();
myObject.recipientName;
myObject.boxnumber.Add(1);
myObject.boxnumber.Add(2);
myObject.boxHeight.Add(1.55);
myObject.boxHeight.Add(2.55);
myObject.boxLength.Add(1.55);
myObject.boxLength.Add(2.55);
myObject.boxWidth.Add(1.55);
myObject.boxWidth.Add(2.55);
bool test = API_Methods.SubmitConsignment(out JSON);
My problem is with the foreach loop - I know the code is incomplete - but I was hoping to iterate through the lists, but even with an empty foreach loop it appears to be the wrong place to put the loop as I start getting syntax errors about an expected "}"
You're actually overcomplicating this for yourself - create complete package objects, and add them to the List Packages, and then pass the rootobject to the serializer.
The error you are getting is because you are not correctly initializing / filling your Packages List. Your object is invalid, hence the serializer is throwing exceptions.
This will be a lot easier for you if you create some constructors for your objects, something like this:
public Package(number, height, length, width)
{
boxNumber = number;
boxHeight = height;
//rest of your properties here in same format
}
You can then also make your setters private in the class, if you wish.
You can then easily create your package objects:
var package1 = new Package(10, 10, 10, 10);
This should make it a lot easier to create your list of boxes to put in your rootObject.
You can add each package to the packages list (individually or within a foreach loop):
Packages.Add(package1)
Or you could even start getting more concise:
Packages.Add(new Package(10,10,10,10));
You want to separate your concerns more to help keep this clear - so I'd recommend you fully construct your rootObject, add the packages to the list in one class (your 3rd code snippet), and then serialize it another (your 2nd code snippet).
Edit:
I think you'd find it easier to refactor your code somewhat:
1) Have a public rootobject in your Json_Api class, with get; set;. Get rid of the box collections. Get rid of your foreach loop from here too.
public class API_Methods
{
public rootObject RootObject { get; set; }
public Boolean SubmitConsignment(out string JSONData)
{
string JSONData = JsonConvert.SerializeObject(NewRequestObject);
return true;
}
}
2) Set the properties of this rootobject outside this class (where you currently initialize your objects). Add the New Package()s to Packages list here too.
API_Methods myObject = new API_Methods();
myObject.RootObject.recipientName = "NAME";
myObject.RootObject.Packages.Add(new Package(10,10,10,10);
myObject.RootObject.Packages.Add(new Package(20,20,20,20);
bool test = API_Methods.SubmitConsignment(out JSON);
3) Call the API method next, it should return a serialized version of the wholerootobject, including your packages.
Just a side note, it would be more conventional to send the RootObject as a parameter to the API, and return the Json string object back.

changing data of class list

I try to add new record to my list and change data of it, but it's change all record of array, here is my class and code:
The class:
public class TransportDto
{
public int type { get; set; }
public string url { get; set; }
public int Relationship { get; set; }
}
loading data to my list:
IQueryable<TransportDto> list = _entities.Database.SqlQuery<TransportDto>(filterExpression).AsQueryable();
List<TransportDto> lst = list.ToList();
TransportDto help =lst[1];// adding record like one of my result
lst.Add(help);
Now I try to change value of lst[lst.Count-1] but when I change it , lst[1] change too
lst[lst.Count-1].type=3;
on result both lst[lst.Count-1] and lst[1] changes to 3, but I just try to change one record of array
When you write TransportDto help =lst[1];, you are getting the object at the index of 1. When you add this object to the list, you are adding the same object to the list not a copy of it.
Which means in the list, the indices of 1 and lst.Count-1 point to the same object. When you access the object as lst[lst.Count-1] you are accessing this object and .type=3 makes changes to the object the two indices are pointing to.
If you want a copy of the object in lst[1], consider cloning the object. This could make a copy of the object. You can refer here: How to Clone Objects
To simplify, in your case you can create a clone function like this:
public class TransportDto
{
public int type { get; set; }
public string url { get; set; }
public int Relationship { get; set; }
public TransportDto Clone(){
return new TransportDto{
type = type,
url = url,
Relationship = Relationship
};
}
}
This clone function creates a new object of TransportDto and passes the same values thus creating a copy. Now in your list you can do this:
TransportDto help =lst[1].Clone();
Now help stores a copy of lst[1].

asp.net null reference error assigning values to object when values are present

I have a search page which is using strongly typed objects, but I have the values broken into specific groups.
Code behind page calls the following when the user clicks the search button (none of these fields are empty):
SearchCriteria sc = new SearchCriteria();
sc.Generic.id = txtId.Text;
sc.Generic.maxReturned = rblMaxReturned.SelectedIndex;
sc.DisplayOnly.category = txtCategory.Text;
sc.DisplayOnly.type = txtType.Text;
sc.Building.address = txtAddress.Text;
sc.Building.city = txtCity.Text;
The DataType file is defined like this:
[Serializable]
public class SearchCriteria
{
public _Generic Generic { get;set; }
[Serializable]
public class _Generic
{
public int id {get;set;}
public int maxReturned {get;set;}
}
public _DisplayOnly DisplayOnly { get;set; }
[Serializable]
public class _DisplayOnly
{
public int category {get;set;}
public int type {get;set;}
}
public _Building Building { get;set; }
[Serializable]
public class _Building
{
public int address {get;set;}
public int city {get;set;}
}
}
When the code executes, I get a nullreferenceerror even though all the items in the various textboxes have a value. However, if I take out the public _Building Building { get;set; } and call the class directly it works and populates the values. What's the best solution here? Should I not use intermediary definition and call the class directly? If so, how can I call the different groups without making four different calls on the code behind page?
You need to initialize the internal class instances. Simply declaring the variables doesn't mean that you can access their properties without creating the instances. You could easily do that in the constructor of the SearchCriteria class
[Serializable]
public class SearchCriteria
{
public SearchCriteria()
{
// Without these initialization the internal variables are all null
// and so assigning any property of a null object causes the error
Generic = new _Generic();
DisplayOnly = new _DisplayOnly()
Building = new _Building();
}
.....
}
When you create a new instance of your SearchCriteria class, the properties are not initialized, and so they all have a value of null. So now look at the very first line where you try to use one of those properties:
sc.Generic.id = txtId.Text;
Here, txtID.Text is perfectly fine, but sc.Generic is null. When you try to look up the it's .id property for assignment, that's where the exception is thrown.
To fix this, you need to initialize each of those properties to have an instance of their type. Additionally, it's probably a good idea to use a private set, like so:
public _Generic Generic { get;private set; }
This will still allow to make all the same assignments that are currently written, because that only requires a get action to retrieve the type instance. The assignment/set operation is on the property of the property.

Categories