c# - Store multiple values in one key with JSON - c#

I'm trying to put multiple values ​​(arrays) in a key, but I do not know how to do this.I've tried:
public class Customer
{
public string Name { get; set; }
public string Bought[5] { get; set; }
}
List<Customer> Products = new List<Customer>();
Customer product = new Customer();
product.Bought[0] = listproducts.SelectedIndex;
product.Bought[1] = listproducts.SelectedIndex;
product.Bought[3] = listproducts.SelectedIndex;
product.Bought[4] = listproducts.SelectedIndex;
product.Bought[5] = listproducts.SelectedIndex;
I know this is wrong, but is a poor example for what I'm trying to do.
I was trying something by this way:
Store multiple values in single key in json
But I do not know how to adapt to C#.
I just need to save to the JSON file something like:
[{"Name":"Bryan", "Products":"car", "boat", "bike"}]
If someone is able to help me, I would be grateful. I would give this further step toward knowledge.

You should just use a List<string> for Bought, or List<BoughtObject>.
That will create
{ // Customer
Name : "Bryan",
Bought: [// Array of your bought objects
{ ... },
{ ... }
]
}
If you just want it to be a single string comma separated (and then split it on the client side or display it like "Boat, Jet, Tv" you could leave it as type of string and then do a .Join(boughtItems)

You could use something like Dictionary<Type1, List<type2> >. In your case you could do Dictionary<string, List<string> >. That way you will able to store multiple values for a key.

Try with this may be it will work
public class Customer
{
public string Name { get; set; }
public readonly string[] Bought = new string[5];
}

Related

How can I list the keys in a nested dictionary?

I'm just starting to learn c# by creating my own program that is basically a damage calculator for the game Risk of Rain 2. As such, right now I am trying to create the user selection for a ability that a survivor has. I am currently using this format for how I store the ability and it's information:
Dictionary<string, Dictionary<string, double[]>> survivorAbilities =
new Dictionary<string, Dictionary<string, double[]>>();
Dictionary<string, double[]> acridAbilities =
new Dictionary<string, double[]>();
An example of some of the data being stored is:
//Epidemic: Special 1
double[] acridEpidemic = new double[3];
acridEpidemic[0] = (1); //instances
acridEpidemic[1] = (1); //damage %
acridEpidemic[2] = (1); //proc
acridAbilities.Add("epidemic", acridEpidemic);
//Initializing Acrid and his abilities
survivorAbilities.Add("acrid", acridAbilities);
The problem is that the survivor "Acrid" is only one of the four survivors that I decided to add. The other survivors have different dictionary names specific to the survivor. For example, instead of "acridAbilities", there is "commandoAbilities". I want to print each key in these dictionaries, but I'm not sure how to specify which survivor's dictionary to pick from, from the dictionary "survivorAbilities".
Sorry if its a bit confusing and long, not quite sure what to put.
This is not a direct answer to your question, but some advice since you're learning C# as well as an alternate approach.
A dictionary of nested dictionaries is, of course, perfectly valid code, but it can be confusing. Instead, why not create some classes to easily encapsulate your data? For example, you have this in your sample code:
acridEpidemic[0] = (1); //instances
acridEpidemic[1] = (1); //damage %
acridEpidemic[2] = (1); //proc
Notice you're using comments to explain what these keys in the dictionary are supposed to represent? This is not good; the code should be able to explain what it's doing without needing comments like these.
Here is an example, based on my understanding of your problem, of what I'd consider a better approach:
public void SomeExample()
{
var survivors = new List<Survivor>(); // all your survivors go in here
var acrid = new Survivor
{
Name = "Acrid",
};
acrid.Abilities.Add(new Ability
{
Name = "epidemic",
Instances = 1,
Damage = 1,
Proc = 1,
});
survivors.Add(acrid);
}
public class Survivor
{
public string Name { get; set; }
public List<Ability> Abilities { get; set; } = new List<Ability>();
}
public class Ability
{
public string Name { get; set; }
public double Instances { get; set; }
public double Damage { get; set; }
public double Proc { get; set; }
}

Nested list of constant strings in C#

Any way I can store this data in a clean way, and preferably use variable names instead of strings as keys to avoid typos? E.g. UNITED_STATES = "201" instead of "United States" = "201".
{
"countries": {
"id": "123",
"data" {
"United States": "201"
"Canada": "202",
}
},
"departments": { ... }
}
I started with KeyValuePairs like this, but nesting data in here seems like a bad idea.
private static readonly List<KeyValuePair<string, string>> CategoryIds = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("Countries", "123"),
new KeyValuePair<string, string>("Departments", "124")
};
Two approaches to deserialize JSON here.
Strong typing approach (good approach):
public class A
{
public B Countries {get;set;}
public C Departments {get;set;}
}
public class B
{
public int Id {get;set;}
public D Data {get;set;}
}
...
var result = JsonConvert.DeserializeObject<A>(json);
You create DTO objects manually and just expect them to deserialize successfully.
Dynamic approach (bad but sometimes acceptable approach):
dynamic result = JsonConvert.DeserializeObject(json);
var data = result.countries.data;
You create some "bag of things" (dynamic is basically a bunch of hierarchical Dictionary wrapped into syntax sugar cane), don't really care about all of them, and just want some of its properties.
Maybe you could use json.net JObject?
It allows you to work with dynamic objects and convert them to and from json strings
Documentation for JObject
https://www.newtonsoft.com/json/help/html/QueryingLINQtoJSON.htm
Nuget:
https://www.nuget.org/packages/Newtonsoft.Json/
You can use a dictionary<k,v> for this purpose along with a enum like below probably
enum CountryVal
{
UnitesStates,
Canada
}
With a model structure like
public class Countries
{
public string id { get; set; }
public Dictionary<CountryVal, int> Data { get; set; }
}
public class Departments
{
public string id { get; set; }
}
public class RootObject
{
public Countries countries { get; set; }
public Departments departments { get; set; }
}
You can create a public class as below and you can then call country value like CountriesConstants.UNITED_STATES in your code and if you need to change the value just update it in CountriesConstants class
public class CountriesConstants
{
public const string UNITED_STATES = "201";
public const string Canada = "202";
//Add More
}
NJsonSchema is a library that will enable you to generate code in csharp as well as few other languages from a standard json schema. It is very powerful and configurable, and can pave most of the way on your behalf. But as i said it will expect an standard json schema as for the source of generation.
var schema = NJsonSchema.JsonSchema4.FromFileAsync(filename);
var generator = new CSharpGenerator(schema.Result);
var file = generator.GenerateFile();
Above is the minimum amount of code required to generate csharp classes from json schema. you can define settings and pass to the generator function to service your special needs of course.
github page for this library:
NJsonSchema github
Nuget page:
NJsonSchema Nuget

Missunderstanding CSV format

First of all, I wanna say: "I know there're XML/JSON/YAML formats, and I know how they works". But now I'm facing a task to make export to CSV format file.
I've read about CSV on wikipedia, searched StackOverflow on CSV topics, and didn't find answer.
As I read, this is popular format for future Excel tables display.
Okay, if I have a simple class with only ValueType properties, it's just fine.
public class MyClass
{
public int ID { get; set; }
public string Name { get; set; }
public string ToCsvString()
{
return string.Format("{0};{1}", ID, Name);
}
public static MyClass FromCsvString(string source)
{
var parts = source.Split(';');
var id = int.Parse(parts[0]);
var name = parts[1];
return new MyClass()
{
ID = id,
Name = name,
};
}
}
But what if I have a little bit more complex class. For example with List<> of other objects.
public class MyClassWithList: MyClass
{
public MyClassWithList()
{
ItemsList = new List<string>();
}
public List<string> ItemsList { get; set; }
public string ToCsvString()
{
// How to format it for future according to CSV format?
return string.Format("{0};{1}", base.ToCsvString(), ItemsList.ToString());
}
public static MyClassWithList FromCsvString(string source)
{
var parts = source.Split(';');
var id = int.Parse(parts[0]);
var name = parts[1];
// How to get it back from CSV formatted string?
var itemsList = parts[2];
return new MyClassWithList()
{
ID = id,
Name = name,
ItemsList = new List<string>()
};
}
}
How should I serialize/deserialize it to CSV?
And final question is how to do the same about when class A contains class B instances?
First off, you have to flatten your data.
If ClassA contains a ClassB, then you'll need to create a flattened POCO that has properties that access any nested properties, e.g. ClassB_PropertyA.
You can really only have 1 variable length property and it has to be the last property, then you can have any column after a point represent a single list property.
Secondly, there is no CSV Serliazation standard. There is https://www.ietf.org/rfc/rfc4180.txt but that only deals with reading text from fields. Something as simple as changing your locale can mess up a CSV library as semicolons will be switched for commas in cultures where a common represents a decimal. There are also many bugs and edge cases in Excel that cause serialization to String to be problematic. And some data is automatically converted to Dates or Times. You need to determine which program you expect to open the CSV and learn about how it handles CSV data.
Once you have a flat POCO, then a CSV is simply a header row with the name of each property followed by a row per object. There are libraries that can help you with this.

Suggesting options from two List<string> objects separated by a comma in C#

I have an AutoSuggestBox in my application. It suggests city names from a List object, which is deserialized from a json file. I also have another List object that contains the country names. What I want to do is suggesting the options in city_name,country_name format. How do I do this?
I suppose there is a way to pass something like DisplayValue on your AutoSuggestBox which must point to a property in your class. Let's say:
List<MyObject> list = new List<MyObject>(); // Simulate already deserialzied list
myAutoSuggestBox.ItemsSource = list;
myAutoSuggestBox.DisplayValue = "Combined";
public class MyObject
{
public string City { get;set; }
public string Country { get;set; }
public string Combined
{
get
{
return $"{City}, {Country}";
}
}
}
P.S. I don't know if it's a desktop or web application, but that's the main idea with a desktop app.

Serializing Name/Value Pairs in a Custom Object via Web Service

This is a very complicated question concerning how to serialize data via a web service call, when the data is not-strongly typed. I'll try to lay it out as best possible.
Sample Storage Object:
[Serializable]
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
public List<NameValuePairs> OtherInfo { get; set; }
}
[Serializable]
public class NameValuePairs {
public string Name { get; set; }
public string Value { get; set; }
}
Sample Use:
[WebMethod]
public List<StorageObject> GetStorageObjects() {
List<StorageObject> o = new List<StorageObject>() {
new StorageObject() {
Name = "Matthew",
Birthday = "Jan 1st, 2008",
OtherInfo = new List<NameValuePairs>() {
new NameValuePairs() { Name = "Hobbies", Value = "Programming" },
new NameValuePairs() { Name = "Website", Value = "Stackoverflow.com" }
}
},
new StorageObject() {
Name = "Joe",
Birthday = "Jan 10th, 2008",
OtherInfo = new List<NameValuePairs>() {
new NameValuePairs() { Name = "Hobbies", Value = "Programming" },
new NameValuePairs() { Name = "Website", Value = "Stackoverflow.com" }
}
}
};
return o;
}
Return Value from Web Service:
<StorageObject>
<Name>Matthew</Name>
<Birthday>Jan 1st, 2008</Birthday>
<OtherInfo>
<NameValuePairs>
<Name>Hobbies</Name>
<Value>Programming</Value>
</NameValuePairs>
<NameValuePairs>
<Name>Website</Name>
<Value>Stackoverflow.com</Value>
</NameValuePairs>
</OtherInfo>
</StorageObject>
What I want:
<OtherInfo>
<Hobbies>Programming</Hobbies>
<Website>Stackoverflow.com</Website>
</OtherInfo>
The Reason & Other Stuff:
First, I'm sorry for the length of the post, but I wanted to give reproducible code as well.
I want it in this format, because I'm consuming the web services from PHP. I want to easily go:
// THIS IS IMPORANT
In PHP => "$Result["StorageObject"]["OtherInfo"]["Hobbies"]".
If it's in the other format, then there would be no way for me to accomplish that, at all. Additionally, in C# if I am consuming the service, I would also like to be able to do the following:
// THIS IS IMPORANT
In C# => var m = ServiceResult[0].OtherInfo["Hobbies"];
Unfortunately, I'm not sure how to accomplish this. I was able to get it this way, by building a custom Dictionary that implemented IXmlSerializer (see StackOverflow: IXmlSerializer Dictionary), however, it blew the WSDL schema out of the water. It's also much too complicated, and produced horrible results in my WinFormsTester application!
Is there any way to accomplish this ? What type of objects do I need to create ? Is there any way to do this /other than by making a strongly typed collection/ ? Obviously, if I make it strongly typed like this:
public class OtherInfo {
public string Hobbies { get; set; }
public string FavoriteWebsite { get; set; }
}
Then it would work perfectly, I would have no WSDL issues, I would be able to easily access it from PHP, and C# (.OtherInfo.Hobbies).
However, I would completely lose the point of NVP's, in that I would have to know in advance what the list is, and it would be unchangeable.. say, from a Database.
Thanks everyone!! I hope we're able to come up with some sort of solution to this. Here's are the requirements again:
WSDL schema should not break
Name value pairs (NVP's) should be serialized into attribute format
Should be easy to access NVP's in PHP by name ["Hobbies"]
Should be easy to access in C# (and be compatible with it's Proxy generator)
Be easily serializable
Not require me to strongly type the data
Now, I am /completely/ open to input on a better/different way to do this. I'm storing some relatively "static" information (like Name), and a bunch of pieces of data. If there's a better way, I'd love to hear it.
This is like dynamic properties for a object.
C# is not quite a dynamic language unlike javascript or maybe PHP can parse the object properties on the fly. The following two methods are what I can think of. The second one might fit into your requirements.
The KISS Way
The Keep It Simple Stupid way
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
public List<string> OtherInfo { get; set; }
}
You can have name value pairs which is separated by '|'
OtherInfo = {"Hobbies|Programming", "Website|Stackoverflow.com"}
Serialized forms
<StorageObject>
<Name>Matthew</Name>
<Birthday>Jan 1st, 2008</Birthday>
<OtherInfo>
<string>Hobbies|Programming</string>
<string>Website|Stackoverflow.com</string>
</OtherInfo>
</StorageObject>
The Dynamic Way in C#
Make the name value pair part become an XML element so that you can build it dynamically.
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
public XElement OtherInfo { get; set; } // XmlElement for dot net 2
}
You can easily build up OtherInfo object as element centric
e.g.
XElement OtherInfo = new XElement("OtherInfo");
OtherInfo.Add( ..Hobbies xelement & text value..);
OtherInfo.Add( ..WebSite xelement & text value..);
The serialized form will be
<OtherInfo>
<Hobbies>Programming</Hobbies>
<Website>Stackoverflow.com</Website>
</OtherInfo>
or build it as attribute centric
XElement OtherInfo = new XElement("OtherInfo");
OtherInfo.Add( ..nvp xattribute Hobbies & value..);
OtherInfo.Add( ..nvp xattribute WebSite & value..);
<OtherInfo>
<nvp n="Hobbies" v="Programming" />
<nvp n="Website" v="Stackoverflow.com" />
</OtherInfo>
For any dynamic language, it can access to the properties directly.
For the rest, they can access the value by read the XML. Reading XML is well supported by most of framework.
This is what I've settled on.
Class Structure:
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
[XmlAnyElement("Info")] // this prevents double-nodes in the XML
public XElement OtherInfo { get; set; }
}
Usage:
StorageObject o = new StorageObject();
o.OtherInfo.Add(new XElement("Hobbies","Programming");
o.OtherInfo.Add(new XElement("Website","Stackoverflow.com");
Output:
<Info>
<Hobbies>Programming</Hobbies>
<Website>Stackoverflow.com</Website>
</Info>
I would like to thank everyone for their assistance, I really appreciate the help and ideas.
As a completely different take on this, why not think about doing it completely differently. Have one web service method to return the serialized storage object, minus the OtherInfo and another method to return the list of properties (keys) for OtherInfo, and a third to return the list of values for any key. Granted, it will take more round trips to the web service if you want all of the data, but the solution will be much simpler and more flexible.
[Serializable]
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
[Nonserializable]
public Dictionary<string,List<string>> OtherInfo { get; set; }
}
[WebMethod]
public List<StorageObject> GetStorageObjects() {
// returns list of storage objects from persistent storage or cache
}
[WebMethod]
public List<string> GetStorageObjectAttributes( string name )
{
// find storage object, sObj
return sObj.Keys.ToList();
}
[WebMethod]
public List<string> GetStorageObjectAtributeValues( sting name, string attribute )
{
// find storage object, sObj
return sObj[attribute];
}
Have a look into the System.Xml.Serialization.XmlSerializerAssemblyAttribute attribute. This lets you specify a custom class-level serializer. You'll be able to spit out whatever XML you like.
A quick way to get up to speed on these is to use sgen.exe to generate one and have a peek at it with Reflector.
-Oisin
I'm not sure this would solve your problem (it would in C#, but maybe not in PHP), but try using Dictionary<string,List<string>> OtherInfo instead of List<NameValuePairs>. Then "Hobbies" and "Websites" would be your keys and the values would be the list of hobbies or web sites. I'm not sure how it would serialize, though.
You would be able to reference the lists of hobbies as:
List<string> hobbies = storageObject.OtherInfo["Hobbies"];
[EDIT] See here for a generic XML serializable dictionary. This derived class is the one you would need to use instead of generic Dictionary.

Categories