C# - Deserialize Json to Dictionary - c#

Im trying to convert my List code lines to Dictionary since as i understand they are faster, and more easy to manage, like i can get its value with its key value rather than just index.
Thus, im trying to modify my json-deserializing code (which actually,Newtonsoft's), but i have no idea how can i convert raw json to dictionary with my own format.
Dictionary <string,Tag>
Tag class is my own class with member variables, contains tag informations.
it doesnt includes functions for sure.
String should be Tag.Name, a string member variable.
//TODO: Fix "Hacky" solution to deserialized lines into Dictionary
//Which means : Raw Json -> Deserialized data -> List -> Dictionary is very hacky in my perspective
//Should be : Raw Json -> Deserialized data -> Dictionary, but i have no idea to convert directly like that
else if (filename.Contains(".json")) // very janky method to detect json file. dont depend on file extension - rather depend on actual file format.
{
string line = String.Join("", Lines);
List<Tag> lv_list = new List<Tag>();
Dictionary<string,Tag> lv_dict = new Dictionary<string, Tag>();
lv_list = JsonConvert.DeserializeObject<List<Tag>>(line);
gVar.Tags.AddRange(lv_list);
gVar.gTagCount += lv_list.Count();
for (int i = 0; i < lv_list.Count(); ++i)
{
gVar.Tags2.Add(lv_list[i].Name, lv_list[i]);
}
int lv_tagcount = 1;
for (int i = 0; i < gVar.gTagCount; ++i)
{
gVar.Tags[i].TagCount = lv_tagcount;
++lv_tagcount;
}

I wouldn't bother deserializing directly to a Dictionary: if the JSON is formatted like an array, go ahead and deserialize it to a List first. There's nothing "hacky" about the two-step process.
Use LINQ's ToDictionary() method to get a dictionary from a list:
Dictionary<string,Tag> lv_dict = JsonConvert.DeserializeObject<List<Tag>>(line)
.ToDictionary(tag => tag.Name);
You seem to have a habit of initializing a variable with an empty collection that never gets used, and then setting the variable to some other value. This isn't helpful: just declare and set the variable at the same time. But based on this pattern I'm wondering if gVar is an object you're trying to initialize even though your code looks like it's trying to add things to that variable. Do you really want to do something more like this?
gVar.Tags2 = JsonConvert.DeserializeObject<List<Tag>>(line)
.ToDictionary(tag => tag.Name);
I'd also question whether the TagCount property is a good pattern. It looks like you're trying to make each Tag explicitly aware of its own position in the collection that it's found in. This is a code smell. Perhaps now that you're using a Dictionary that won't be necessary because you can look up the tag by its name in the Dictionary?

Related

How to manipulate objects after JSON list deserialization

I'm trying to work with JSON files to store a Class and I'm stuck with the deserialization.
I'm using the following NameSpace:
using System.Text.Json.Serialization;
I have a very simple class, made of 2 properties:
public EnumOfType Type { get; set; }
public double Price { get; set; }
I have 4 instances of this classe that I store in a list. When quiting the application, this list is saved in a JSON file.
string jsonString;
jsonString = JsonSerializer.Serialize(myListOfInstances);
File.WriteAllText(FileName, jsonString);
When I'm opening the Application, I want the JSON file to be loaded to recreate the instances.
I'm using the following method, which apparently works well.
string jsonString = File.ReadAllText(FileName);
myListOfInstances = JsonSerializer.Deserialize<List<MyClass>>(jsonString);
So far so good. When I check the content of the list, it is correctly populated and my 4 instances are there.
But then... how to use them?
Before the JSON, I was creating each instance (for example:)
MyClass FirstInstance = New MyClass();
FirstInstance.Type = EnumOfType.Type1;
FirstInstance.Price = 100.46;
Then I could manipulate it easily, simply calling FirstInstance.
myWindow.Label1.Content = FirstInstance.Price.ToString("C");
FirstInstance.Method1...
Now that the instances are in my list, I don't know how to manipulate them individually because I don't know how to call them.
It's probably obvious to most, but I'm still in the learning process.
Thank you for your help,
Fab
Based on how you have loaded the JSON file into your program, it looks like your variable myListOfInstances already contains all four MyClass objects ready to go. At this point you can use List accessors (or Linq if you want to be fancy) and do things such as the following:
myListOfInstances[0] //Gives you the first item in the list accessed by index
myListOfInstances.First() //Gives you the first item in the list (using linq)
foreach(var item in myListOfInstances) {
// this will iterate through all four items in the list storing each instance in
//the 'item' variable
}
etc...
EDIT: From my comment below. If you need to access values in a a list directly, you can search for specific conditions in the list using linq with the 'Where' method. The syntax is something like this:
myListOfInstances.Where(x => x.Property == SomePropertyToMatch)

How can I deserialize a Yaml object containing strings into a List<string>?

I created a Yaml with filenames, so I can make my program check if every file of the list exists. I haven"t done much with yaml yet, and the documentations don't really help me.
This is my Yaml (It's pretty small):
DLLs:
- Filename1
- Filename2
- Filename3
At the moment, this is my code:
using (var reader = new StringReader(File.ReadAllText("./Libraries/DLLList.yml")))
{
/*
* List<string> allDllsList = deserialized yaml.getting all values of the "DLLs"-list
*/
var deserializer = new Deserializer();
var dlls = deserializer.Deserialize<dynamic>(reader)["DLLs"] as List<Object>;
/*This gives me the Error "Object System.Collections.Generic.Dictionary`2[System.Object,System.Object] cannot be converted into "System.String""*/
List<string> allDllsList = dlls.Cast<String>().ToList();
}
Can someone explain to me how I can get the values out of the Yaml file, and why it works in the way you do it?
Edit: Now it works, I used the wrong yaml, I had 2 versions
First, take the return value from deserializer.Deserialize<dynamic>(reader) and inspect it in the debugger. It's a Dictionary<String, Object>, and it's got an entry named "DLLs" that contains a List<Object>. The objects in that list are all strings. There you go:
var dlls = deserializer.Deserialize<dynamic>(reader)["DLLs"] as List<Object>;
// Use .Cast<String>() as shown if you want to throw an exception when there's something
// that does not belong there. If you're serious about validation though, that's a bit
// rough and ready.
// Use .OfType<String>() instead if you want to be permissive about additional stuff
// under that key.
List<string> allDllsList = dlls.Cast<String>().ToList();

Clarification needed regarding basic indexer and collection in C#

I'm going through another developer's code, which is shown below:
[XmlElement("AdminRecipient")] public AdminRecipient[] AdminRecipientCollection = new AdminRecipient[0];
public AdminRecipient this[ string type ]
{
get
{
AdminRecipient result = null;
foreach( AdminRecipient emailRecipient in AdminRecipientCollection )
{
if( emailRecipient.Type == type )
{
result = emailRecipient;
break;
}
}
return( result );
}
Can someone explain what's going to happen in this line?
public AdminRecipient[] AdminRecipientCollection = new AdminRecipient[0];
The XML file that contains all of the email recipients has about 5 email addresses. But by using [0], will the foreach loop return each of those email addresses?
I have a basic understanding of indexers, but I don't this. What does it do?:
public AdminRecipient this[ string type ]
At the end of the day, the problem here is that the application doesn't send out an email when all 5 recipients are in the xml file. If I replace the 5 addresses with just 1 email addresses, then I'm able to get the email (which leads me to believe that there's a logic issue somewhere here).
An indexer allows you to use a type with the same syntax as array access. One of the simplest examples would be List<T>:
List<string> x = new List<string>();
x.Add("Item 0");
x.Add("Item 1");
string y = x[0]; // "Item 0"
That use of x[0] is calling the indexer for List<T>. Now for List<T> the index is an integer, as it is for an array, but it doesn't have to be. For example, with Dictionary<TKey, TValue> it's the key type:
Dictionary<string, string> dictionary = new Dictionary<string, string>();
dictionary.Add("foo", "bar");
string fetched = dictionary["foo"]; // fetched = "bar"
You can have a "setter" on an indexer too:
dictionary["a"] = "b";
Your indexer is just returning the first AdminRecipient in the array with a matching type - or null if no match can be found.
(It's unfortunate that the code you've shown is also using a public field, by the way. It would be better as a property - and probably not an array, either. But that's a separate discussion.)
EDIT: Regarding the first line you highlighted:
public AdminRecipient[] AdminRecipientCollection = new AdminRecipient[0];
That will create an array with no elements, and assign a reference to the AdminRecipientCollection field. With no further changes, the foreach loop would not have anything to iterate over, and the indexer will always return null.
However, presumably something else - such as XML serialization - is assigning a different value to that field - populating it with more useful data.
The line you're asking about is an assignment of AdminRecipientCollection to an empty array. This assignment will occur at instantiation time. The deserialization of the XML will happen after the class is instantiated, if ever. The author's purpose here is presumably to ensure that AdminRecipientCollection will never be null, even in case the class is used before XML is deserialized into it.
By ensuring that AdminRecipientCollection is never null, the author can ignore the possibility that the foreach loop will throw a NullReferenceException. At least, that's probably the hope.
Unfortunately, because AdminRecipientCollection is public, the AdminRecipient property could still throw, as in the following case:
var demoClass = new DemoClass();
demoClass.AdminRecipientCollection = null;
var firstRecipient = demoClass[0];
To prevent this possibility, remove the initial assignment and check for null in the AdminRecipient property before beginning the enumeration.

Converting a KeyValuePair collection in to anonymous type

Is it possible to convert a a IEnumerable<KeyValuePair<string,string>> of KeyValuePair to an anonymous type?
Dictionary<string, string> dict= new Dictionary<string, string>();
dict.add("first", "hello");
dict.add("second", "world");
var anonType = new{dict.Keys[0] = dict[0], dict.Keys[1] = dict[1]};
Console.WriteLine(anonType.first);
Console.WriteLine(anonType.second);
********************output*****************
hello
world
The reason i would like to do this is because I am retrieving an object from a webservice that represents an object that does not exist in the wsdl. The returned object only contains a KeyValuePair collection that contains the custom fields and their values. These custom fields can be named anything, so i cant really map an xml deserialization method to the final object i will be using (whose properties must be bound to a grid).
*Just because I used Dictionary<string,string> does not mean it is absolutely a dictionary, i just used it for illustration. Really its an IEnumerable<KeyValuePair<string,string>>
Ive been trying to thing of a way to do this, but am drawing a blank. This is c# .NET 4.0.
You could use the ExpandoObject, it is based on a dictionary.
I think there are a lot of ways to achieve this, but actually converting it in the same Dictionary seems a bit odd to do.
One way to accomplish this, by not actually converting everyting is the following:
public class MyDictionary<T,K> : Dictionary<string,string> // T and K is your own type
{
public override bool TryGetValue(T key, out K value)
{
string theValue = null;
// magic conversion of T to a string here
base.TryGetValue(theConvertedOfjectOfTypeT, out theValue);
// Do some magic conversion here to make it a K, instead of a string here
return theConvertedObjectOfTypeK;
}
}
ExpandoObject is the best option, which I believe is a wrapper around some XML. You could also use an XElement:
var result = new XElement("root");
result.Add(new XElement("first", "hello"));
result.Add(new XElement("second", "world"));
Console.WriteLine(result.Element("first").Value);
Console.WriteLine(result.Element("second").Value);
foreach (var element in result.Elements())
Console.WriteLine(element.Name + ": " + element.Value);
I haven't used ExpandoObject, so I'd try that first because I understand it does exactly what you want and is also something new and interesting to learn.

Can you Instantiate an Object Instance from JSON in .NET?

Since Object Initializers are very similar to JSON, and now there are Anonymous Types in .NET. It would be cool to be able to take a string, such as JSON, and create an Anonymous Object that represents the JSON string.
Use Object Initializers to create an Anonymous Type:
var person = new {
FirstName = "Chris",
LastName = "Johnson"
};
It would be awesome if you could pass in a string representation of the Object Initializer code (preferably something like JSON) to create an instance of an Anonymous Type with that data.
I don't know if it's possible, since C# isn't dynamic, and the compiler actually converts the Object Initializer and Anonymous Type into strongly typed code that can run. This is explained in this article.
Maybe functionality to take JSON and create a key/value Dictionary with it would work best.
I know you can serialize/deserializer an object to JSON in .NET, but what I'm look for is a way to create an object that is essentially loosely typed, similarly to how JavaScript works.
Does anyone know the best solution for doing this in .NET?
UPDATE: Too clarify the context of why I'm asking this... I was thinking of how C# could better support JSON at the language level (possibly) and I was trying to think of ways that it could be done today, for conceptual reasons. So, I thought I'd post it here to start a discussion.
There are languages for .NET that have duck-typing but it's not possible with C# using Dot.Notation since C# requires that all member references are resolved at compile time. If you want to use the Dot.Notation, you still have to define a class somewhere with the required properties, and use whatever method you want to instantiate the class from the JSON data. Pre-defining a class does have benefits like strong typing, IDE support including intellisense, and not worrying about spelling mistakes. You can still use anonymous types:
T deserialize<T>(string jsonStr, T obj) { /* ... */}
var jsonString = "{FirstName='Chris', LastName='Johnson, Other='unused'}";
var person = deserialize(jsonString, new {FirstName="",LastName=""});
var x = person.FirstName; //strongly-typed
You should check out the JSON.net project:
http://james.newtonking.com/pages/json-net.aspx
You are basically talking about the ability to hydrate an object from JSON, which this will do. It won't do the anonymous types, but maybe it will get you close enough.
I wrote a relatively short method that will Parse JSON and return a name/value Dictionary that can be accessed similarly to the actual object in JavaScript.
Here's a sample usage of the below method:
var obj = ParseJsonToDictionary("{FirstName: \"Chris\", \"Address\":{Street:\"My Street\",Number:123}}");
// Access the Address.Number value
object streetNumber = ((Dictionary<string, object>)obj["Address"])["Number"];
And, here's the code for the ParseJsonToDictionary method:
public static Dictionary<string, object> ParseJsonToDictionary(string json)
{
var d = new Dictionary<string, object>();
if (json.StartsWith("{"))
{
json = json.Remove(0, 1);
if (json.EndsWith("}"))
json = json.Substring(0, json.Length - 1);
}
json.Trim();
// Parse out Object Properties from JSON
while (json.Length > 0)
{
var beginProp = json.Substring(0, json.IndexOf(':'));
json = json.Substring(beginProp.Length);
var indexOfComma = json.IndexOf(',');
string endProp;
if (indexOfComma > -1)
{
endProp = json.Substring(0, indexOfComma);
json = json.Substring(endProp.Length);
}
else
{
endProp = json;
json = string.Empty;
}
var curlyIndex = endProp.IndexOf('{');
if (curlyIndex > -1)
{
var curlyCount = 1;
while (endProp.Substring(curlyIndex + 1).IndexOf("{") > -1)
{
curlyCount++;
curlyIndex = endProp.Substring(curlyIndex + 1).IndexOf("{");
}
while (curlyCount > 0)
{
endProp += json.Substring(0, json.IndexOf('}') + 1);
json = json.Remove(0, json.IndexOf('}') + 1);
curlyCount--;
}
}
json = json.Trim();
if (json.StartsWith(","))
json = json.Remove(0, 1);
json.Trim();
// Individual Property (Name/Value Pair) Is Isolated
var s = (beginProp + endProp).Trim();
// Now parse the name/value pair out and put into Dictionary
var name = s.Substring(0, s.IndexOf(":")).Trim();
var value = s.Substring(name.Length + 1).Trim();
if (name.StartsWith("\"") && name.EndsWith("\""))
{
name = name.Substring(1, name.Length - 2);
}
double valueNumberCheck;
if (value.StartsWith("\"") && value.StartsWith("\""))
{
// String Value
d.Add(name, value.Substring(1, value.Length - 2));
}
else if (value.StartsWith("{") && value.EndsWith("}"))
{
// JSON Value
d.Add(name, ParseJsonToDictionary(value));
}
else if (double.TryParse(value, out valueNumberCheck))
{
// Numeric Value
d.Add(name, valueNumberCheck);
}
else
d.Add(name, value);
}
return d;
}
I know this method may be a little rough, and it could probably be optimized quite a bit, but it's the first draft and it just works.
Also, before you complain about it not using regular expressions, keep in mind that not everyone really understands regular expressions, and writing it that way would make in more difficult for others to fix if needed. Also, I currently don't know regular expression too well, and string parsing was just easier.
You can't return an anonymous type from a method**, so a "rehydrated" anonymous type's existence would be limited to the method in which it is rehydrated. Kind of pointless.
** You can return it as an object (which requires reflection to access its properties--yeech) or you can "cast it by example", which is pointless as well, since it takes extra steps and it means you already KNOW what the object's type should look like, so why not just create an object and fill it up in the first place?
What is the application for this?
I would not go down this road for a few reasons.
First; it may require a lot of support code using reflection and such to create the transparent method that you are talking about.
Second, like you said, C# is a strongly typed language and things like these were left out of the language specification for a reason.
Third, the overhead for doing this would not be worth it. Remember that web pages (especially AJAX queries) should be really fast or it defeats the purpose. If you go ahead and spend 50% serializing your objects between C# and Javascript then you have a problem.
My solution would be to create a class that just encapsulates a dictionary and that takes a JSON string as a ctor argument. Then just extend that class for each type of JSON query you want to handle. This will be a strongly typed and faster solution but still maintain extensibility and ease of use. The downside is that there is more code to write per type of JSON request.
:)

Categories