Reversing the save method in the Open method - c#

Fairly new to programming. I just can't wrap my head around how to get this to work in reverse. I have a save method and an open method.
Save:
IDictionary<string, IDictionary<string, object>> pluginStates = new Dictionary<string, IDictionary<string, object>>();
signaller.RaiseSaveRequest(pluginStates); <--goes out and gets packed plugins
//loop through plugins to get values and types
//holds all of the plugin arrays
Dictionary<string, object> dictProjectState = new Dictionary<string, object>();
foreach (KeyValuePair<string,IDictionary<string,object>> plugin in pluginStates)
{
//holds jsonRepresented values
Dictionary<string, object> dictJsonRep = new Dictionary<string, object>();
//holds object types
Dictionary<string, object> dictObjRep = new Dictionary<string, object>();
object[] arrayDictHolder = new object[2]; //holds all of the dictionaries
string pluginKey = plugin.Key;
IDictionary<string, object> pluginValue = new Dictionary<string, object>();
pluginValue = plugin.Value;
foreach (KeyValuePair<string, object> element in pluginValue)
{
string jsonRepresentation = JsonConvert.SerializeObject(element);
object objType = element.Value.GetType().ToString();
dictJsonRep.Add(element.Key, jsonRepresentation);
dictObjRep.Add(element.Key, objType);
}
arrayDictHolder[0] = dictJsonRep;
arrayDictHolder[1] = dictObjRep;
dictProjectState.Add(pluginKey, arrayDictHolder);
}
JsonSerializer serializer = new JsonSerializer();
using (StreamWriter sw = new StreamWriter(strPathName))
using (JsonWriter writer = new JsonTextWriter(sw))
{
serializer.Serialize(writer, dictProjectState);
}
So, when someone saves, an event handler goes out and gets the packedState of each plugin, adds it to a dictionary pluginStates. I then go through each plugin in the pluginStates, adding the key, and json string version of the value to 1 dictionary and the key, object type to another dictionary, add those 2 dictionaries to an array and then pack up a dictionary that contains the pluginKey and the array for each plugin. Reasoning: when deserializing, I'm hitting problems going from JArray to type DataTable and other types that are within the dictionary that gets passed back to the plugin to unpack itself upon opening. I'm trying to figure out how to reverse this, so that when user opens project, I have the dictProjectState and need to bring it all the way back through the code to end up with 1 dictionary containing the plugins. How do I mirror this save in the open??
Thanks!

I believe when you deserialize in the open function, you should get an object that can be cast to Dictionary<string, object>.
The keys of this dictionary would be the names of the plugins in your final dictioary (pluginKey in yoru save method). The values should be the object[] arrayDictHolder objects. You can cast them to object[] and then get at the two values inside (dictJsonRep and dictObjRep in your save function).
Once those values are both cast to Dictionary<string, object>(), you can iterate through the KeyValuePairs in one of them, and use the key from those pairs to get at the value store in the other dictionary.
Code would be similar to the following:
var pluginStates = new Dictionary<string, IDictionary<string, object>>();
var dictProjectState = jsonSerializer.Deserialize(reader) as Dictionary<string, object>;
foreach(var projectStatePair in dictProjectState)
{
string pluginKey = projectStatePair.Key;
var pluginValues = new Dictionary<string, object>();
object[] arrayDictHolder = projectStatePair.Value as object[];
var dictJsonRep = arrayDictHolder[0] as Dictionary<string, object>;
var dictObjRep = arrayDictHolder[1] as Dictionary<string, object>;
foreach(var JsonRepPair in dictJsonRep)
{
string jsonRepresentation = JsonRepPair.Value;
// We know both dictionaries have the same keys, so
// get the objType for this JsonRepresentation
object objType = dictObjRep[JsonRepPair.Key];
// Since you're serializing objType to a string, you'll need
// to get the actual Type before calling DeserializeObject.
Type jsonType = System.Type.GetType(objType as string);
object value = JsonConvert.DeserializeObject( jsonRepresentation, jsonType );
pluginValues.Add( JsonRepPair.Key );
}
pluginStates.Add( pluginKey, pluginValues );
}
Note: You are currently setting objType to element.Value.GetType().ToString(); in your save function. You will either need to update save() to set it to element.Value.GetType(); or convert the type strings back to actual Types before trying to deserialize.
Note: I am not very familiar with the Json libraries. The above code is focused on creating your pluginStates dictionary out of the deserialized results. You will need to handle the creation of your JsonSerializer and make sure you close any streams after you've deserialized them. You may also need to tweak the actual deserialization calls to get the desired results.
Update: Added conversion of objType to actual System.Type object.

Related

Dealing with complex objects

I'm relatively new to C# programming and have had my first encounter with objects. A method that I am using is returning an object and I am setting it to an object of my own as such,
object target = searchResponse.Result.Parameters.Values;
From here I'm attempting to extract the data from the object, but it seems to be a "complex" object (just came up with that terminology, it's probably wrong, but I don't know the correct term). According to the Visual Studio locals menu, the object's value is count = 2. But, 'inside' the object is the data that I want, as shown below:
How would I get to these pieces of data?
As #UnholySheep suggested use var wherever possible if you don't know the DataType beforehand.
But for instance as you have stored the data in target and as in the picture it's of type Dictionary, you can cast it
Dictionary<string, object> dict = target as Dictionary<string, object>;
Now you can access your data from dict
EDIT 1:
I assumed you may want to know how to access data from a Dictionary, so here is a short snippet:
Dictionary<int, string> myDictionary = new Dictionary<int, string>();
//here _int_ will be the key, _string_ will be your data
myDictionary.Add(1, "abc"); //1 is key, abc is data
myDictionary.Add(2, "def");
myDictionary.Add(3, "ghk");
string myData = myDictionary[2]; //pass the value to be fetched
//myData = def

Serializing ExpandoObject with json.Net

I have the following code, where page.Fields is an ExpandoObject. I'm iterating through some user-defined properties and adding them to an Expando, cast to an IDictionary<string,string> to allow me to add new field name / values dynamically, but when I set the Fields property to the value of props, serializing afterwards only gives names with blank values of {}. Why?
page.Fields.Foo = "asdf";
var test = JsonConvert.SerializeObject(page); // shows Foo=asdf in the json
// attach all fields to the page object, casting to an IDictionary to be able to add var names
var props = new ExpandoObject() as IDictionary<string, Object>;
foreach (string key in Request.Form.Keys)
{
if (key.StartsWith("Fields."))
{
var fieldName = key.Substring(key.IndexOf(".") + 1);
props.Add(fieldName, Request.Form[key]);
}
}
var test2 = JsonConvert.SerializeObject(props); // blank values of {}
page.Fields = props as ExpandoObject;
// loses the values for the Fields property
test = JsonConvert.SerializeObject(page);
UPDATE the curse of Nancy strikes, the Request.Form values turned out to be dynamic, so I had to .ToString() it to make it fit into the expected IDictionary<string,string>
To correctly serialize the data you must declare the variable as dynamic, not as an ExpandoObject, JSON .net uses reflection to retrieve properties, if it's a dynamic it casts it as an ExpandoObject and uses the keys as property names, but if you pass the ExpandoObject directly it tries to retrieve the properties from the ExpandoObject type.
Just change
var props = new ExpandoObject() as IDictionary<string, Object>;
to
var props = new ExpandoObject();
var iProps = props as IDictionary<string, Object>;
Use iProps to add the data and pass props to the serialization.
EDIT:
You're storing the value in "Page.Fields", this must be dynamic also.
I'm suspect it's a defect and you are not getting anything for Request.Form.Keys that's matches Field. criteria.
your code works fine for me if I've a page class with dynamic Fields property

Fastest of the following Mapping objects (Dictionary,Tuple,KeyvaluePair)

Currently i have a mapping setup like this
//Identifiers to save (currently)
Dictionary<string, Dictionary<string, string>> toSaveIdentifiers =
new Dictionary<string, Dictionary<string, string>>(); //
however, i want to add an extra dimension to it because i just missed out on an extra attribute to add.
I'm trying to set up some form of mapping that gets populated frequently in a program and is looked up throughout the program as well. I was wondering what is the best way to go about doing this.
//Identifiers to save (tuple)
Dictionary<Tuple<string,string>, Dictionary<string, string>> toSaveIdentifiers =
new Dictionary<Tuple<string, string>, Dictionary<string, string>>(); //
//Identifiers to save (adding another dictionary dimension)
Dictionary<string, Dictionary<string,Dictionary<string, string>>> toSaveIdentifiers =
new Dictionary<string, Dictionary<string, Dictionary<string, string>>>(); //
//Identifiers to save (adding keyvaluepair)
Dictionary<KeyValuePair<string,string>, Dictionary<string, string>> toSaveIdentifiers =
new Dictionary<KeyValuePair<string, string>, Dictionary<string, string>>(); //
When i populate it/lookup i do something like.
// check identifier map dictionary
if (dictionary.Keys.Contains(identifier))
{
if (dictionary[identifier].Keys.Contains(currency))
{
//stuff
}
else
{
//stuff
}
}
else
{
//more stuff
}
What would be the best method of doing this for lookup?
Since your identifiers all seem to be of type string, you could always just concat them all into one big string and use that as the key. Then instead of doing the nested Contains, you'd just have to do one. It'd also be more flexible as far a storing different levels of identifiers.
i.e. Given a 2 level key, it'd be
string ident = level1Identifier + "." + level2Identifier;
(using string.format() or StringBuilder would be more efficient, but this code is better for explaining)
Also consider the joining character should be something you know won't show up in any levels identifier to avoid confusion or accidently duplicates.

Are there any JSON serializers for C# .Net 4.0+ that serialize JSON to Dictionary<string,dynamic> and dynamic[]

I know you can serialize JSON to Dictionary and object[], but the problem is that it requires you to constantly cast things all over the place:
using System.Web.Script.Serialization;
var json = new JavaScriptSerializer { }.DeserializeObject(#"{array:['item1','item2']}") as Dictionary<string, object>;
var strings = new List<string> { };
if (json != null)
{
var array = json["array"] as object[];
if (array != null)
{
foreach (var item in array.Select(i=>i as string))
{
if (!string.IsNullOrWhiteSpace(item))
{
strings.Add(item);
}
}
}
}
return strings.ToArray();
I would much rather do something more like:
try
{
var json = new JavaScriptSerializer { }.DeserializeDynamic(#"{array:['item1','item2']}") as Dictionary<string, dynamic>;
var strings = new List<string>{};
foreach (var item in json["array"])
strings.Add(item as string);
return strings.ToArray();
}
catch { return null; }
I have run into problems with object[] and number types, I'm never sure if it's going to be an int, a float, a decimal, or a double etc. I can't sanitize my json data so it reliably is only a double...
Are there any JSON Serializers for .Net 4.0 that output Dictionary and dynamic[]? That just seams like a more natural choice for working with json then Dictionary and object[]...
JSON.NET should handle your needs (with the Json.NET Dynamic Extensions).
Dictionary<string, object> and Dictionary<string, dynamic> are the same types as far runtime is concerned.
For example this works:
var dynDic =new Dictionary<string,object>{ {"test", 1}}
as Dictionary<string,dynamic>;
int value = dynDic["test"];
So if your first works:
var json = new JavaScriptSerializer { }
.DeserializeObject(#"{array:['item1','item2']}")
as Dictionary<string, object>;
then so should:
var json = new JavaScriptSerializer { }
.DeserializeDynamic(#"{array:['item1','item2']}")
as Dictionary<string, dynamic>;
But then :
foreach (var item in json["array"])
strings.Add(item as string);
doesn't use dynamic invocation so it compiles that same whether the item var was object or dynamic so if you are having problems my guess is it's in the use of dynamic.
Same rule applies to object[] and dynamic[], they are the same type, you can freely cast between them, when you cast you are just telling the compiler how to use the objects from that array.
dynamic[] dynArr = new object[]{1};
So the answer is that anything that will deserialize to Dictionary<string,object> or object[] can be used as Dictionary<string,dynamic> or dynamic[]

C# get the types defining a Dictionary at run time

I was wondering what is the best way for getting the generic arguments that definine a dictionary at run time is.
Take for example:
Dictionary<string, object> dict;
How at runtime can I find out that the keys are strings?
I'm not sure if I understand your question correctly but I think you mean something like this:
Dictionary<string, object> dict = new Dictionary<string, object>();
// ...
var args = dict.GetType().GetGenericArguments();
// args[0] will be typeof(string)
Here's an NUnit test to demonstrate Mehrdad's answer, and with a dictionary containing integers as keys, and strings as values:
[Test]
public void testGetPhysicalTypeForGenericDictionary()
{
IDictionary<int, string> myDictionary = new Dictionary<int, string>();
Type [] myTypes = myDictionary.GetType().GetGenericArguments();
Assert.AreEqual(2, myTypes.Length);
var varTypes = myDictionary.GetType().GetGenericArguments();
Assert.AreEqual("Int32", varTypes[0].Name);
Assert.AreEqual("System.Int32", varTypes[0].FullName);
Assert.AreEqual("String", varTypes[1].Name);
Assert.AreEqual("System.String", varTypes[1].FullName);
}

Categories

Resources