Flatten nested Dictionary<string, object> - c#

I am deserializing some nested JSON with the following:
string json = #"{
""name"": ""charlie"",
""someID"": 123,
""level1"" : {
""name"": ""charlie 1"",
""someID"": 456
}
}";
JavaScriptSerializer serializer = new JavaScriptSerializer();
Dictionary<string, object> data = serializer.Deserialize<Dictionary<string, object>>(json);
Once this is done, the values of each dictionary key may be another Dictionary, and so on, multiple levels deep.
What I would like to do is to flatten the multi-level data so it's just a flat Array/List, with just all the JSON attribute names and their values. So that I end up with something like this:
name, "charlie"
someID, 123
name, charlie 1
someID, 456
I was heading down the path of using SelectMany() and so forth, but could not wrangle it to do what I am after.
I've been sort of waddling around with things like this:
var obj = data.Values.SelectMany<object, Dictionary<string, object>>(x => x);
But I am not able to satisfy the compiler. Yes, I am lost.
I am using .NET 3.5.

Func<Dictionary<string, object>, IEnumerable<KeyValuePair<string, object>>> flatten = null;
flatten = dict => dict.SelectMany(kv =>
kv.Value is Dictionary<string,object>
? flatten((Dictionary<string,object>)kv.Value)
: new List<KeyValuePair<string,object>>(){ kv}
);
var flatList = flatten(data).ToList();

You need recursion here:
IEnumerable<Tuple<string, string>> Flatten(this IDictionary dict)
{
foreach(DictionaryEntry kvp in dict)
{
var childDictionary = kvp.Value as IDictionary;
if(childDictionary != null)
{
foreach(var tuple in childDictionary.Flatten())
yield return tuple;
}
else
yield return Tuple.Create(kvp.Key.ToString(), kvp.Value.ToString());
}
}
// Usage:
var flatList = data.Flatten().ToList();
On .NET 3.5, you can use KeyValuePair<string, string> instead of Tuple<string, string>.
Please note that there is no KeyValuePair.Create, you need to use new KeyValuePair<string, string>(kvp.Key.ToString(), kvp.Value.ToString()) instead.

Related

Using Json.NET to convert part of a JObject to Dictionary<string, string>

I'm testing out Json.Net in preparation for a different project, and I'm having some trouble. What I want to do is convert the content of moretests to a Dictionary. Here's my full code:
class Program
{
static void Main(string[] args)
{
string json = #"{
'test': 'a',
'test2': 'b',
'moretests':{
'test3': 'c',
'test4': 'd'
}
}";
JObject parsed = JObject.Parse(json);
IDictionary<string, JToken> results = (JObject)parsed["moretests"];
Dictionary<string, string> results2 = results.ToDictionary(pair => pair.Key, pair => (string)pair.Value);
foreach (var i in results.Keys)
{
Console.WriteLine($"{i}: {results[i]}");
}
}
}
I got these 2 lines:
IDictionary<string, JToken> results = (JObject)parsed["moretests"];
Dictionary<string, string> results2 = results.ToDictionary(pair => pair.Key, pair => (string)pair.Value);
from here but I was wondering if it's possible to shorten it to one line. I tried doing
Dictionary<string, string> results = (JObject)parsed["moretests"].ToDictionary(pair => pair.Key, pair => pair.Value)
but it didn't work as in that case pair is no longer a KeyValuePair but instead a JToken. Can anybody help me out?
You could use this line
Dictionary<string, string> results = ((IDictionary<string, JToken>)(JObject)parsed["moretests"]).ToDictionary(pair => pair.Key, pair => (string)pair.Value);
You may want to avoid doing this though, it really hurts readability.
Edit
I messed around with it for a while and got this cleaner version.
Dictionary<string, string> results = JsonConvert.DeserializeObject<Dictionary<string, string>>(parsed["moretests"].ToString());

C# deserialize json into dictionary and reverse KV

I am trying to deserialize json and reverse the key/value with the least amount of complexity. I have an input json that looks like:
{
"1":"apple",
"2":"banana",
"3":"orange",
"4":"grape"
}
And want to serialize this into a Dictionary<string,string>, but reverse the dictionary so the fruit is the key and the integers are the value. Any way to achieve that with Newtonsoft.Json?
var obj = JsonConvert.DeserializeObject<Dictionary<string,string>>(json);
// obj.Keys = { "1", "2", "3" }, but I want them to be { "apple", "banana", ... }
The least complex way would be to just create another dictionary from the one you've deserialised:
JsonConvert.DeserializeObject<Dictionary<string,string>>(json)
.ToDictionary(x => x.Value, x => x.Key);
var obj = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
Dictionary<string, string> temp = new Dictionary<string, string>();
Parallel.ForEach(obj, (item)=> {
temp.Add(item.Value, item.Key);
});
This is the simplified way you can achieve this .

How to write Dictionary contents to a textfile

I have declared a dictionary like below--
Dictionary<int, string> dic = new Dictionary<int, string>()
{
{977,"String1"},
{1021,"String2"},
{784,"String3"},
{801, "String4"}
};
In my textfile value will be contain like this-
977,"String1",
1021,"String2",
784,"String3",
801, "String4"
I want to store above dictionary keys and values manually in a text file and want to access like dictionary by for loop.
Like this--
foreach (KeyValuePair<int, string> pair in dic)
{
if (pair.Key == any_integer_value_to_compare)
{
Console.WriteLine(pair.Value);
}
}
How can I do that??
You can use serialization/De-serialization to store and retrieve dictionary data from a XML file. It's better method as far as performance is concerned.
Here is how you can implement that:
Declare class Item:
public class item
{
[XmlAttribute]
public int id;
[XmlAttribute]
public string value;
}
Your Dictionary:
Dictionary<int, string> dict = new Dictionary<int, string>()
{
{977,"String1"},
{1021,"String2"},
{784,"String3"},
{801, "String4"}
};
Create Serializer object:
XmlSerializer serializer = new XmlSerializer(typeof(item[]),newXmlRootAttribute() { ElementName = "items" });
Serialization:
serializer.Serialize(stream,dict.Select(kv=>new item(){id = kv.Key,value=kv.Value}).ToArray() );
Deserialization:
var myDict = ((item[])serializer.Deserialize(stream)).ToDictionary(i =>i.id, i => i.value);
Accessing your dictionary values
foreach (KeyValuePair<int, string> pair in myDict )
{
if (pair.Key == any_integer_value_to_compare)
{
Console.WriteLine(pair.Value);
}
}
In this way you can systematically store the state of your dictionary values at the particular instance and access it later on by de-serializing the XML file.
To create or override a CSV file (note: there're no spaces and quotes)
977,String1
1021,String2
784,String3
801,String4
you can use Linq
File.WriteAllLines(#"C:\MyTest\MyFile.txt", dic
.Select(pair => String.Join(",", pair.Key, pair.Value)));
If you want to add quotations for values, add them (the simplest implementation)
File.WriteAllLines(#"C:\MyTest\MyFile.txt", dic
.Select(pair => String.Join(",", pair.Key, "\"" + pair.Value + "\"")));
you can't add spaces since line
{801, "String4"},
is totally equals to
{801,"String4"},
In case you want to convert the dictionary into String (and then combine it
with some other text(s) in order ro put into text file):
String text = String.Join(Environment.NewLine, dic
.Select(pair => String.Join(",", pair.Key, pair.Value))

Concat two dictionaries so that original's shared keys are updated

Say I have two dictionaries:
Dictionary<string, string> orig = new Dictionary <string, string>();
orig.Add("one", "value one");
orig.Add("two", "");
orig.Add("three", "");
Dictionary<string, string> newDict = new Dictionary <string, string>();
newDict.Add("one", "this value should not be added");
newDict.Add("two", "value two");
newDict.Add("three", "value three");
How can I merge the two dictionaries so that the resulting dictionary updates the keys only where their corresponding values are empty? Additionally, the merge should not add any keys that are present in new but not in orig. That is, "one" still has the value "value one" while "two" and "three" are updated with the values from new.
I tried using orig.Concat(new);, but that leaves me with the original dictionary. Perhaps this can be done with LINQ?
Try:
orig = orig.Keys.ToDictionary(c => c, c=>(orig[c] == "" ? newDict[c] : orig[c]));
This loop does what you want efficiently and readable:
Dictionary<string, string> result = new Dictionary<string, string>();
foreach (var keyVal in orig)
{
if (!string.IsNullOrEmpty(keyVal.Value))
result.Add(keyVal.Key, keyVal.Value);
else
{
string val2;
if (newDict.TryGetValue(keyVal.Key, out val2))
result.Add(keyVal.Key, val2);
else
result.Add(keyVal.Key, "");
}
}
Result:
one, value one
two, value two
three, value three
I would use the foreach
foreach (var pair in orig.Where(x=> string.IsNullOrEmpty(x.Value)).ToArray())
{
orig[pair.Key] = newone[pair.Key];
}
Extension method 'one-liners' are great when they help to clarify intention, but for something like this, I would be inclined to write a small method with an explicit loop that does the desired operation. I think this is much cleaner than creating a new dictionary using various extension method transformations:
public void PopulateMissingValues(Dictionary<string, string> orig, Dictionary<string, string> newDict)
{
foreach (var pair in orig.Where(p => p.Value == string.Empty))
{
string newValue;
if (newDict.TryGetValue(pair.Key, out newValue))
orig[pair.Key] = newValue;
}
}

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[]

Categories

Resources