I am working with a cut down version of C# 3.5 on a platform that doesn't have the flexibility of using third party libraries.
While I can parse JSON (using a json stream reader), I am not sure how to actually turn it into a class. (there is also no access to the usual json to class deserializer).
Does anyone know how to use reflection to manually (yet dynamically) turn a JSON string into a class?
sample Json:
{"items":[
{"firstName":"bob", "lastName":"smith", "id":1001, "foods": [{"name":"fish", "name":"bacon", "name":"cereal"}]},
{"firstName":"sarah", "lastName":"smith", "id":1002, "foods": [{"name":"bacon", "name":"apples", "name":"chocolate"}]},
{"firstName":"tom", "lastName":"waffle", "id":1003, "foods": [{"name":"waffles", "name":"sticks", "name":"stones"}]},
{"firstName":"reginald", "lastName":"hamtuft", "id":1003, "foods": [{"name":"ham", "name":"cigars", "name":"noisy children"}]}
]}
Thanks Pete and the rest for getting me on the right track for this. In my case I also had to deserialize a JSON string to a Strongly Typed object in a SQL CLR Function, so I was limited on the libraries I could use "safely" (more info here).
I modified the code for ParseJSON, which deserializes into a Dictionary<string, object> to be able to deserialize arrays of arrays, which it couldn't do, I also developed some methods to cast the resulting Dictionary into a Strongly Typed Object without using the JavaScriptConverter or the System.Runtime.Serialization library, with this code we are able to do the following:
//we have a foo and bar classes with a variety of fields and properties
private class foo
{
public List<double[][]> data;
public IEnumerable<object> DataObj;
public int integerField;
public long longProperty { get; set; }
public string stringValue;
public int? nullableInt;
public DateTime dateTimeValue;
public List<bar> classValues;
}
private class bar
{
public string stringValue;
public DateTimeOffset dateTimeOffsetValue;
}
static void Main(string[] args)
{
//lets deserialize the following JSON string into our foo object,
//the dictionary is optional, and not necessary if our JSON property names are the same as in our object.
//in this case it's used to map the "jdata" property on the JSON string to the "data" property of our object,
//in the case of the "dataObj", we are mapping to the uppercase field of our object
string JSONstring = "{\"jdata\":[[[1526518800000,7.0],[1526518834200,7.0]],[[1526549272200,25.0],[1526549306400,25.0]]],\"dataObj\":[[[1526518800000,7.0],[1526518834200,7.0]],\"abc\",123],\"integerField\":623,\"longProperty\":456789,\"stringValue\":\"foo\",\"nullableInt\":\"\",\"dateTimeValue\":\"2018-05-17T01:00:00.0000000\", \"classValues\": [{\"stringValue\":\"test\",\"dateTimeOffsetValue\":\"2018-05-17T05:00:00.0000000\"},{\"stringValue\":\"test2\",\"dateTimeOffsetValue\":\"2018-05-17T06:00:00.0000000\"}]}";
var mappingDict = new Dictionary<string, string>() { { "jdata", "data" }, { "dataObj", "DataObj" } };
foo myObject = ParseJSON<foo>(JSONstring, mappingDict);
}
The ParseJSON method will take a JSON string as input and optionally a Dictionary<string, string> and will attempt to cast it into the Type T the dictionary is used to map any property on the JSON string into a property of the object (for example, the "jdata"/"data" dictionary declared above).
public static T ParseJSON<T>(string jsonString, Dictionary<string, string> mappingTable = null)
{
Dictionary<string, object> jsonDictionary = ParseJSON(jsonString);
T castedObj = CastAs<T>(jsonDictionary, mappingTable);
return castedObj;
}
The following is my modified method for JSON parsing (can parse arrays of arrays):
public static Dictionary<string, object> ParseJSON(string json)
{
int end;
return ParseJSON(json, 0, out end);
}
private static Dictionary<string, object> ParseJSON(string json, int start, out int end)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
bool escbegin = false;
bool escend = false;
bool inquotes = false;
string key = null;
int cend;
StringBuilder sb = new StringBuilder();
Dictionary<string, object> child = null;
List<object> arraylist = null;
Regex regex = new Regex(#"\\u([0-9a-z]{4})", RegexOptions.IgnoreCase);
int autoKey = 0;
int subArrayCount = 0;
List<int> arrayIndexes = new List<int>();
bool inSingleQuotes = false;
bool inDoubleQuotes = false;
for (int i = start; i < json.Length; i++)
{
char c = json[i];
if (c == '\\') escbegin = !escbegin;
if (!escbegin)
{
if (c == '"' && !inSingleQuotes)
{
inDoubleQuotes = !inDoubleQuotes;
inquotes = !inquotes;
if (!inquotes && arraylist != null)
{
arraylist.Add(DecodeString(regex, sb.ToString()));
sb.Length = 0;
}
continue;
}
else if (c == '\'' && !inDoubleQuotes)
{
inSingleQuotes = !inSingleQuotes;
inquotes = !inquotes;
if (!inquotes && arraylist != null)
{
arraylist.Add(DecodeString(regex, sb.ToString()));
sb.Length = 0;
}
continue;
}
if (!inquotes)
{
switch (c)
{
case '{':
if (i != start)
{
child = ParseJSON(json, i, out cend);
if (arraylist != null)
{
arraylist.Add(child);
}
else
{
dict.Add(key.Trim(), child);
key = null;
}
i = cend;
}
continue;
case '}':
end = i;
if (key != null)
{
if (arraylist != null) dict.Add(key.Trim(), arraylist);
else dict.Add(key.Trim(), DecodeString(regex, sb.ToString().Trim()));
}
return dict;
case '[':
if (arraylist != null)
{
List<object> _tempArrayList = arraylist;
for (int l = 0; l < subArrayCount; l++)
{
if (l == subArrayCount - 1)
{
_tempArrayList.Add(new List<object>());
}
else
{
_tempArrayList = (List<object>)_tempArrayList[arrayIndexes[l]];
}
}
if (arrayIndexes.Count < subArrayCount)
{
arrayIndexes.Add(0);
}
subArrayCount++;
}
else
{
arraylist = new List<object>();
subArrayCount++;
}
continue;
case ']':
if (key == null)
{
key = "array" + autoKey.ToString();
autoKey++;
}
if (arraylist != null)
{
List<object> _tempArrayList = arraylist;
for (int l = 0; l < subArrayCount; l++)
{
if (l == subArrayCount - 1)
{
if (sb.Length > 0)
{
_tempArrayList.Add(sb.ToString());
}
subArrayCount--;
if (subArrayCount == arrayIndexes.Count)
{
if (arrayIndexes.Count > 0)
{
arrayIndexes[arrayIndexes.Count - 1]++;
}
}
else if (subArrayCount == arrayIndexes.Count - 1)
{
arrayIndexes.RemoveAt(arrayIndexes.Count - 1);
if (arrayIndexes.Count > 0)
{
arrayIndexes[arrayIndexes.Count - 1]++;
}
}
}
else
{
_tempArrayList = (List<object>)_tempArrayList[arrayIndexes[l]];
}
}
sb.Length = 0;
}
if (subArrayCount == 0)
{
dict.Add(key.Trim(), arraylist);
arraylist = null;
key = null;
}
continue;
case ',':
if (arraylist == null && key != null)
{
dict.Add(key.Trim(), DecodeString(regex, sb.ToString().Trim()));
key = null;
sb.Length = 0;
}
if (arraylist != null && sb.Length > 0)
{
List<object> _tempArrayList = arraylist;
for (int l = 0; l < subArrayCount; l++)
{
if (l == subArrayCount - 1)
{
_tempArrayList.Add(sb.ToString());
}
else
{
_tempArrayList = (List<object>)_tempArrayList[arrayIndexes[l]];
}
}
sb.Length = 0;
}
continue;
case ':':
key = DecodeString(regex, sb.ToString());
sb.Length = 0;
continue;
}
}
}
sb.Append(c);
if (escend) escbegin = false;
if (escbegin) escend = true;
else escend = false;
}
end = json.Length - 1;
return dict; //shouldn't ever get here unless the JSON is malformed
}
private static string DecodeString(Regex regex, string str)
{
return Regex.Unescape(regex.Replace(str, match => char.ConvertFromUtf32(Int32.Parse(match.Groups[1].Value, System.Globalization.NumberStyles.HexNumber))));
}
The following methods attempt to cast the returned dictionary from the previous method into a Strong Typed Object, I know is lengthy but it does the job:
private static T CastAs<T>(Dictionary<string, object> source, Dictionary<string, string> mappingTable = null)
{
T outputData = (T)Activator.CreateInstance(typeof(T));
TrySet(outputData, source, mappingTable);
return outputData;
}
private static void TrySet(object target, Dictionary<string, object> source, Dictionary<string, string> mappingTable = null)
{
if (target == null)
{
throw new ArgumentNullException("target");
}
bool useMappingTable = mappingTable != null && mappingTable.Count > 0;
foreach (KeyValuePair<string, object> kv in source)
{
string propertyName = null;
if (useMappingTable && mappingTable.ContainsKey(kv.Key))
{
propertyName = mappingTable[kv.Key];
}
else
{
propertyName = kv.Key;
}
if (!string.IsNullOrEmpty(propertyName))
{
UpdateMember(target, propertyName, kv.Value, mappingTable);
}
}
}
private static void UpdateMember(object target, string propertyName, object value, Dictionary<string, string> mappingTable)
{
try
{
FieldInfo fieldInfo = target.GetType().GetField(propertyName);
if (fieldInfo != null)
{
value = ConvertTo(value, fieldInfo.FieldType, mappingTable);
fieldInfo.SetValue(target, value);
}
else
{
PropertyInfo propInfo = target.GetType().GetProperty(propertyName);
if (propInfo != null)
{
value = ConvertTo(value, propInfo.PropertyType, mappingTable);
propInfo.SetValue(target, value);
}
}
}
catch (Exception ex)
{
throw ex;
}
}
private static object ConvertTo(object value, Type targetType, Dictionary<string, string> mappingTable)
{
try
{
bool isNullable = false;
Type sourceType = value.GetType();
//Obtain actual type to convert to (this is necessary in case of Nullable types)
if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
isNullable = true;
targetType = targetType.GetGenericArguments()[0];
}
if (isNullable && string.IsNullOrWhiteSpace(Convert.ToString(value)))
{
return null;
}
//if we are converting from a dictionary to a class, call the TrySet method to convert its members
else if (targetType.IsClass && sourceType.IsGenericType && sourceType.GetGenericTypeDefinition() == typeof(Dictionary<,>))
{
//make sure our value is actually a Dictionary<string, object> in order to be able to cast
if (sourceType.GetGenericArguments()[0] == typeof(string))
{
object convertedValue = Activator.CreateInstance(targetType);
TrySet(convertedValue, (Dictionary<string, object>)value, mappingTable);
return convertedValue;
}
return null;
}
else if (IsCollection(value))
{
Type elementType = GetCollectionElementType(targetType);
if (elementType != null)
{
if (targetType.BaseType == typeof(Array))
{
return ConvertToArray(elementType, value, mappingTable);
}
else
{
return ConvertToList(elementType, value, mappingTable);
}
}
else
{
throw new NullReferenceException();
}
}
else if (targetType == typeof(DateTimeOffset))
{
return new DateTimeOffset((DateTime)ChangeType(value, typeof(DateTime)));
}
else if (targetType == typeof(object))
{
return value;
}
else
{
return ChangeType(value, targetType);
}
}
catch (Exception ex)
{
if (targetType.IsValueType)
{
return Activator.CreateInstance(targetType);
}
return null;
}
}
private static Array ConvertToArray(Type elementType, object value, Dictionary<string, string> mappingTable)
{
Array collection = Array.CreateInstance(elementType, ((ICollection)value).Count);
int i = 0;
foreach (object item in (IEnumerable)value)
{
try
{
collection.SetValue(ConvertTo(item, elementType, mappingTable), i);
i++;
}
catch (Exception ex)
{
//nothing here, just skip the item
}
}
return collection;
}
private static IList ConvertToList(Type elementType, object value, Dictionary<string, string> mappingTable)
{
Type listType = typeof(List<>);
Type constructedListType = listType.MakeGenericType(elementType);
IList collection = (IList)Activator.CreateInstance(constructedListType);
foreach (object item in (IEnumerable)value)
{
try
{
collection.Add(ConvertTo(item, elementType, mappingTable));
}
catch (Exception ex)
{
//nothing here, just skip the item
}
}
return collection;
}
private static bool IsCollection(object obj)
{
bool isCollection = false;
Type objType = obj.GetType();
if (!typeof(string).IsAssignableFrom(objType) && typeof(IEnumerable).IsAssignableFrom(objType))
{
isCollection = true;
}
return isCollection;
}
private static Type GetCollectionElementType(Type objType)
{
Type elementType;
Type[] genericArgs = objType.GenericTypeArguments;
if (genericArgs.Length > 0)
{
elementType = genericArgs[0];
}
else
{
elementType = objType.GetElementType();
}
return elementType;
}
private static object ChangeType(object value, Type castTo)
{
try
{
return Convert.ChangeType(value, castTo);
}
catch (Exception ex)
{
//if the conversion failed, just return the original value
return value;
}
}
I hope this is helpful to anyone still looking for a way to do this.
Okay, I'm redoing my answer based on the feedback. The dynamic object generator code still comes from this:
Deserialize JSON into C# dynamic object?
This uses RegEx, Generic collections and it does use Linq, but only in 2 lines and those can easily rewritten to not use Linq (the two 'result = ' lines at the end of DynamicJsonObject.TryGetMember()). The generic dictionaries can also be replaced with hash tables if necessary.
The json parser is adapted from How can I deserialize JSON to a simple Dictionary<string,string> in ASP.NET?
class Program
{
static void Main(string[] args)
{
string data = "{ 'test': 42, 'test2': 'test2\"', 'structure' : { 'field1': 'field1', 'field2': 44 } }";
dynamic x = new DynamicJsonObject(JsonMaker.ParseJSON(data));
Console.WriteLine(x.test2);
Console.WriteLine(x.structure.field1);
Console.ReadLine();
}
}
public class DynamicJsonObject : DynamicObject
{
private readonly IDictionary<string, object> _dictionary;
public DynamicJsonObject(IDictionary<string, object> dictionary)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
_dictionary = dictionary;
}
public override string ToString()
{
var sb = new StringBuilder();
ToString(sb);
return sb.ToString();
}
private void ToString(StringBuilder sb)
{
sb.Append("{");
var firstInDictionary = true;
foreach (var pair in _dictionary)
{
if (!firstInDictionary)
sb.Append(",");
firstInDictionary = false;
var value = pair.Value;
var name = pair.Key;
if (value is string)
{
sb.AppendFormat("\"{0}\":\"{1}\"", name, value);
}
else if (value is IDictionary<string, object>)
{
sb.AppendFormat("\"{0}\":", name);
new DynamicJsonObject((IDictionary<string, object>)value).ToString(sb);
}
else if (value is ArrayList)
{
sb.Append("\"");
sb.Append(name);
sb.Append("\":[");
var firstInArray = true;
foreach (var arrayValue in (ArrayList)value)
{
if (!firstInArray)
sb.Append(",");
firstInArray = false;
if (arrayValue is IDictionary<string, object>)
new DynamicJsonObject((IDictionary<string, object>)arrayValue).ToString(sb);
else if (arrayValue is string)
sb.AppendFormat("\"{0}\"", arrayValue);
else
sb.AppendFormat("{0}", arrayValue);
}
sb.Append("]");
}
else
{
sb.AppendFormat("\"{0}\":{1}", name, value);
}
}
sb.Append("}");
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (!_dictionary.TryGetValue(binder.Name, out result))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}
var dictionary = result as IDictionary<string, object>;
if (dictionary != null)
{
result = new DynamicJsonObject(dictionary);
return true;
}
var arrayList = result as ArrayList;
if (arrayList != null && arrayList.Count > 0)
{
if (arrayList[0] is IDictionary<string, object>)
result = new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x)));
else
result = new List<object>(arrayList.Cast<object>());
}
return true;
}
}
public static class JsonMaker
{
public static Dictionary<string, object> ParseJSON(string json)
{
int end;
return ParseJSON(json, 0, out end);
}
private static Dictionary<string, object> ParseJSON(string json, int start, out int end)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
bool escbegin = false;
bool escend = false;
bool inquotes = false;
string key = null;
int cend;
StringBuilder sb = new StringBuilder();
Dictionary<string, object> child = null;
List<object> arraylist = null;
Regex regex = new Regex(#"\\u([0-9a-z]{4})", RegexOptions.IgnoreCase);
int autoKey = 0;
bool inSingleQuotes = false;
bool inDoubleQuotes = false;
for (int i = start; i < json.Length; i++)
{
char c = json[i];
if (c == '\\') escbegin = !escbegin;
if (!escbegin)
{
if (c == '"' && !inSingleQuotes)
{
inDoubleQuotes = !inDoubleQuotes;
inquotes = !inquotes;
if (!inquotes && arraylist != null)
{
arraylist.Add(DecodeString(regex, sb.ToString()));
sb.Length = 0;
}
continue;
}
else if (c == '\'' && !inDoubleQuotes)
{
inSingleQuotes = !inSingleQuotes;
inquotes = !inquotes;
if (!inquotes && arraylist != null)
{
arraylist.Add(DecodeString(regex, sb.ToString()));
sb.Length = 0;
}
continue;
}
if (!inquotes)
{
switch (c)
{
case '{':
if (i != start)
{
child = ParseJSON(json, i, out cend);
if (arraylist != null) arraylist.Add(child);
else
{
dict.Add(key.Trim(), child);
key = null;
}
i = cend;
}
continue;
case '}':
end = i;
if (key != null)
{
if (arraylist != null) dict.Add(key.Trim(), arraylist);
else dict.Add(key.Trim(), DecodeString(regex, sb.ToString().Trim()));
}
return dict;
case '[':
arraylist = new List<object>();
continue;
case ']':
if (key == null)
{
key = "array" + autoKey.ToString();
autoKey++;
}
if (arraylist != null && sb.Length > 0)
{
arraylist.Add(sb.ToString());
sb.Length = 0;
}
dict.Add(key.Trim(), arraylist);
arraylist = null;
key = null;
continue;
case ',':
if (arraylist == null && key != null)
{
dict.Add(key.Trim(), DecodeString(regex, sb.ToString().Trim()));
key = null;
sb.Length = 0;
}
if (arraylist != null && sb.Length > 0)
{
arraylist.Add(sb.ToString());
sb.Length = 0;
}
continue;
case ':':
key = DecodeString(regex, sb.ToString());
sb.Length = 0;
continue;
}
}
}
sb.Append(c);
if (escend) escbegin = false;
if (escbegin) escend = true;
else escend = false;
}
end = json.Length - 1;
return dict; //theoretically shouldn't ever get here
}
private static string DecodeString(Regex regex, string str)
{
return Regex.Unescape(regex.Replace(str, match => char.ConvertFromUtf32(Int32.Parse(match.Groups[1].Value, System.Globalization.NumberStyles.HexNumber))));
}
}
Thanks again to Pete and the other guys for their brilliant post. I have wrapped mine around a SQL Server CLR scalar function which was incredibly useful in interrogating JSON stored in relational tables (I know some would say just use MongoDB!).
Please see below:
public class JsonHelper
{
/// <summary>
/// Parses the JSON.
/// Thanks to http://stackoverflow.com/questions/14967618/deserialize-json-to-class-manually-with-reflection
/// </summary>
/// <param name="json">The json.</param>
/// <returns></returns>
public static Dictionary<string, object> DeserializeJson(string json)
{
int end;
return DeserializeJson(json, 0, out end);
}
/// <summary>
/// Parses the JSON.
/// </summary>
/// <param name="json">The json.</param>
/// <param name="start">The start.</param>
/// <param name="end">The end.</param>
/// <returns></returns>
private static Dictionary<string, object> DeserializeJson(string json, int start, out int end)
{
var dict = new Dictionary<string, object>();
var escbegin = false;
var escend = false;
var inquotes = false;
string key = null;
var sb = new StringBuilder();
List<object> arraylist = null;
var regex = new Regex(#"\\u([0-9a-z]{4})", RegexOptions.IgnoreCase);
var autoKey = 0;
var inSingleQuotes = false;
var inDoubleQuotes = false;
for (var i = start; i < json.Length; i++)
{
var c = json[i];
if (c == '\\') escbegin = !escbegin;
if (!escbegin)
{
if (c == '"' && !inSingleQuotes)
{
inDoubleQuotes = !inDoubleQuotes;
inquotes = !inquotes;
if (!inquotes && arraylist != null)
{
arraylist.Add(DecodeString(regex, sb.ToString()));
sb.Length = 0;
}
continue;
}
if (c == '\'' && !inDoubleQuotes)
{
inSingleQuotes = !inSingleQuotes;
inquotes = !inquotes;
if (!inquotes && arraylist != null)
{
arraylist.Add(DecodeString(regex, sb.ToString()));
sb.Length = 0;
}
continue;
}
if (!inquotes)
{
switch (c)
{
case '{':
if (i != start)
{
int cend;
var child = DeserializeJson(json, i, out cend);
if (arraylist != null)
{
arraylist.Add(child);
}
else
{
dict.Add(key.Trim(), child);
key = null;
}
i = cend;
}
continue;
case '}':
end = i;
if (key != null)
{
if (arraylist != null) dict.Add(key.Trim(), arraylist);
else dict.Add(key.Trim(), DecodeString(regex, sb.ToString().Trim()));
}
return dict;
case '[':
arraylist = new List<object>();
continue;
case ']':
if (key == null)
{
key = "array" + autoKey;
autoKey++;
}
if (arraylist != null && sb.Length > 0)
{
arraylist.Add(sb.ToString());
sb.Length = 0;
}
dict.Add(key.Trim(), arraylist);
arraylist = null;
key = null;
continue;
case ',':
if (arraylist == null && key != null)
{
dict.Add(key.Trim(), DecodeString(regex, sb.ToString().Trim()));
key = null;
sb.Length = 0;
}
if (arraylist != null && sb.Length > 0)
{
arraylist.Add(sb.ToString());
sb.Length = 0;
}
continue;
case ':':
key = DecodeString(regex, sb.ToString());
sb.Length = 0;
continue;
}
}
}
sb.Append(c);
if (escend) escbegin = false;
escend = escbegin;
}
end = json.Length - 1;
return dict; // theoretically shouldn't ever get here
}
/// <summary>
/// Decodes the string.
/// </summary>
/// <param name="regex">The regex.</param>
/// <param name="str">The STR.</param>
/// <returns></returns>
private static string DecodeString(Regex regex, string str)
{
return
Regex.Unescape(regex.Replace(str,
match =>
char.ConvertFromUtf32(Int32.Parse(match.Groups[1].Value,
System.Globalization.NumberStyles
.HexNumber))));
}
/// <summary>
/// Returns true if string has an "appearance" of being JSON-like
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsJson(string input)
{
input = input.Trim();
return input.StartsWith("{") && input.EndsWith("}")
|| input.StartsWith("[") && input.EndsWith("]");
}
}
The CLR function is below:
/// <summary>
/// Json "extractor" capable of extracting a value of a key using the object notation.
/// </summary>
/// <param name="json"></param>
/// <param name="key"></param>
/// <returns></returns>
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString fn_GetKeyValue(SqlString json, SqlString key)
{
var jsonString = json.ToString();
// Return if N/A
if (string.IsNullOrEmpty(jsonString) || !JsonHelper.IsJson(jsonString))
{
return json;
}
var keyString = key.ToString();
var jsonDictionary = JsonHelper.DeserializeJson(jsonString);
var lastNode = string.Empty;
foreach (var node in keyString.Split('.'))
{
if (!jsonDictionary.ContainsKey(node)) continue;
var childDictionary = jsonDictionary[node] as Dictionary<string, object>;
if (childDictionary != null)
{
jsonDictionary = childDictionary;
}
lastNode = node;
}
if (!jsonDictionary.ContainsKey(lastNode))
{
return null;
}
var keyValueString = jsonDictionary[lastNode].ToString();
return keyValueString == "null" ? null : new SqlString(keyValueString);
}
Usage would be:
-- Example 1 (querying a parent node)
SELECT dbo.fn_GetKeyValue('{
"ExchangeRates": {
"GBP": "1.2",
"USD": "2.0"
},
"Timestamp": "2015-04-10"
}', 'Timestamp');
-- Example 2 (querying a child node using a dot notation)
SELECT dbo.fn_GetKeyValue('{
"ExchangeRates": {
"GBP": "1.2",
"USD": "2.0"
},
"Timestamp": "2015-04-10"
}', 'ExchangeRates.GBP');
Related
from this Answer I learned how to flatten a JSON object in c#.
from JSON String:
{"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}}
To:
The following are lines of strings, not an object
menu.id:file
menu.value:File
menu.popup.menuitem[0].value:New
menu.popup.menuitem[0].onclick:CreateNewDoc()
menu.popup.menuitem[1].value:Open
menu.popup.menuitem[1].onclick:OpenDoc()
menu.popup.menuitem[2].value:Close
menu.popup.menuitem[2].onclick:CloseDoc()
Now, i want to reverse the process.
I can found implementations from this question but it is in JavaScript.
How do I unflatten (return structured JSON from lines) it in C# with json.net?
I managed to solve it out.
Below is my code combined with Sarath Rachuri's flattening code.
I did not test it in too many cases, so it could be buggy.
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace JSONHelper
{
class JSONFlattener
{
private enum JSONType{
OBJECT, ARRAY
}
public static Dictionary<string, string> Flatten(JObject jsonObject)
{
IEnumerable<JToken> jTokens = jsonObject.Descendants().Where(p => p.Count() == 0);
Dictionary<string, string> results = jTokens.Aggregate(new Dictionary<string, string>(), (properties, jToken) =>
{
properties.Add(jToken.Path, jToken.ToString());
return properties;
});
return results;
}
public static JObject Unflatten(IDictionary<string, string> keyValues)
{
JContainer result = null;
JsonMergeSettings setting = new JsonMergeSettings();
setting.MergeArrayHandling = MergeArrayHandling.Merge;
foreach (var pathValue in keyValues)
{
if (result == null)
{
result = UnflatenSingle(pathValue);
}
else
{
result.Merge(UnflatenSingle(pathValue), setting);
}
}
return result as JObject;
}
private static JContainer UnflatenSingle(KeyValuePair<string, string> keyValue)
{
string path = keyValue.Key;
string value = keyValue.Value;
var pathSegments = SplitPath(path);
JContainer lastItem = null;
//build from leaf to root
foreach (var pathSegment in pathSegments.Reverse())
{
var type = GetJSONType(pathSegment);
switch (type)
{
case JSONType.OBJECT:
var obj = new JObject();
if (null == lastItem)
{
obj.Add(pathSegment,value);
}
else
{
obj.Add(pathSegment,lastItem);
}
lastItem = obj;
break;
case JSONType.ARRAY:
var array = new JArray();
int index = GetArrayIndex(pathSegment);
array = FillEmpty(array, index);
if (lastItem == null)
{
array[index] = value;
}
else
{
array[index] = lastItem;
}
lastItem = array;
break;
}
}
return lastItem;
}
public static IList<string> SplitPath(string path){
IList<string> result = new List<string>();
Regex reg = new Regex(#"(?!\.)([^. ^\[\]]+)|(?!\[)(\d+)(?=\])");
foreach (Match match in reg.Matches(path))
{
result.Add(match.Value);
}
return result;
}
private static JArray FillEmpty(JArray array, int index)
{
for (int i = 0; i <= index; i++)
{
array.Add(null);
}
return array;
}
private static JSONType GetJSONType(string pathSegment)
{
int x;
return int.TryParse(pathSegment, out x) ? JSONType.ARRAY : JSONType.OBJECT;
}
private static int GetArrayIndex(string pathSegment)
{
int result;
if (int.TryParse(pathSegment, out result))
{
return result;
}
throw new Exception("Unable to parse array index: " + pathSegment);
}
}
}
Purely System.Text.Json solution for unflatteing JSON. Requires .Net 6.
private static JsonNode Unflatten(Dictionary<string, JsonValue> source)
{
var regex = new System.Text.RegularExpressions.Regex(#"(?!\.)([^. ^\[\]]+)|(?!\[)(\d+)(?=\])");
JsonNode node = JsonNode.Parse("{}");
foreach (var keyValue in source)
{
var pathSegments = regex.Matches(keyValue.Key).Select(m => m.Value).ToArray();
for (int i = 0; i < pathSegments.Length; i++)
{
var currentSegmentType = GetSegmentKind(pathSegments[i]);
if (currentSegmentType == JsonValueKind.Object)
{
if (node[pathSegments[i]] == null)
{
if (pathSegments[i] == pathSegments[pathSegments.Length - 1])
{
node[pathSegments[i]] = keyValue.Value;
node = node.Root;
}
else
{
var nextSegmentType = GetSegmentKind(pathSegments[i + 1]);
if (nextSegmentType == JsonValueKind.Object)
{
node[pathSegments[i]] = JsonNode.Parse("{}");
}
else
{
node[pathSegments[i]] = JsonNode.Parse("[]");
}
node = node[pathSegments[i]];
}
}
else
{
node = node[pathSegments[i]];
}
}
else
{
if (!int.TryParse(pathSegments[i], out int index))
{
throw new Exception("Cannot parse index");
}
while (node.AsArray().Count - 1 < index)
{
node.AsArray().Add(null);
}
if (i == pathSegments.Length - 1)
{
node[index] = keyValue.Value;
node = node.Root;
}
else
{
if (node[index] == null)
{
var nextSegmentType = GetSegmentKind(pathSegments[i + 1]);
if (nextSegmentType == JsonValueKind.Object)
{
node[index] = JsonNode.Parse("{}");
}
else
{
node[index] = JsonNode.Parse("[]");
}
}
node = node[index];
}
}
}
}
return node;
}
private static JsonValueKind GetSegmentKind(string pathSegment) =>
int.TryParse(pathSegment, out _) ? JsonValueKind.Array : JsonValueKind.Object;
To flatten a JSON object:
arrayJSON.stringify()
Hello I got many methods (below 2 examples) that look almost exactly the same. The difference is in name of JSON breanch that is processed, type of returned list and type of objects added to list. I know these example methods yet needs some optimization in its body, but the case is to pass type of returned value and type of class which method currently need and make it all work. If it is possible I would like to avoid casting in place of calling method.
Method 1
public static List<Box> JsonToListOfBoxes(string data)
{
List<Box> ListOfBoxes = new List<Box>();
if(!string.IsNullOrEmpty(data))
{
JObject productsJson = JObject.Parse(data);
JToken jtkProduct;
jtkProduct = productsJson["boxes"];
if(jtkProduct != null)
if(jtkProduct.HasValues)
{
int childrenCount = productsJson["boxes"].Count();
for(int x = 0;x < childrenCount;x++)
ListOfBoxes.Add(new Box(productsJson["boxes"][x]));
}
}
return ListOfBoxes;
}
Method 2
public static List<Envelope> JsonToListOfEnvelopes(string data)
{
List<Envelope> ListOfEnvelopes = new List<Envelope>();
if(!string.IsNullOrEmpty(data))
{
JObject productsJson = JObject.Parse(data);
JToken jtkProduct;
jtkProduct = productsJson["envelopes"];
if(jtkProduct != null)
if(jtkProduct.HasValues)
{
int childrenCount = productsJson["envelopes"].Count();
for(int x = 0;x < childrenCount;x++)
ListOfEnvelopes.Add(new Envelope(productsJson["envelopes"][x]));
}
}
return ListOfEnvelopes;
}
Using generics you can change as follows : (without parameterized generic constructor)
public static List<T> JsonToListOfEnvelopes<T>(string data, string searchString, Func<string, T> creator)
{
List<T> ListOfEnvelopes = new List<T>();
if (!string.IsNullOrEmpty(data))
{
JObject productsJson = JObject.Parse(data);
JToken jtkProduct;
jtkProduct = productsJson[searchString];
if (jtkProduct != null)
if (jtkProduct.HasValues)
{
int childrenCount = productsJson[searchString].Count();
for (int x = 0; x < childrenCount; x++)
ListOfEnvelopes.Add(creator(productsJson[searchString][x]));
}
}
return ListOfEnvelopes;
}
And you can call it as
var result = JsonToListOfEnvelopes("data", "boxes", c => { return new Box(c); });
var result = JsonToListOfEnvelopes("data", "envelopes", c => { return new Envelope(c); });
You could make generic method where dataName should be "boxes" or "envelopes":
public static List<T> JsonToListOfBoxes<T>(string data, string dataName)
{
List<T> ListOfItems = new List<T>();
if (!string.IsNullOrEmpty(data))
{
JObject productsJson = JObject.Parse(data);
JToken jtkProduct;
jtkProduct = productsJson[dataName];
if (jtkProduct != null)
if (jtkProduct.HasValues)
{
int childrenCount = productsJson[dataName].Count();
for (int x = 0; x < childrenCount; x++)
ListOfItems.Add((T)Activator.CreateInstance(typeof(T), productsJson[dataName][x]));
}
}
return ListOfItems;
}
Use example:
var list1 = JsonToListOfBoxes<Box>("dataString", "boxes");
var list2 = JsonToListOfBoxes<Envelope>("dataString", "envelopes");
I just changed #msmolcic logic a bit.
public static List<T> JsonToListOfBoxes<T>(string data)
{
List<T> ListOfItems = new List<T>();
string dataName = typeof(T) == typeof(Box) ? "boxes" : "envelopes";
//if there are many types one can try in below way..
// if (typeof(T) == typeof(Box))
// {
// dataName = "Box";
// }
// else if (typeof(T) == typeof(Envelope))
// {
// dataName = "envelopes";
// }
if (!string.IsNullOrEmpty(data))
{
JObject productsJson = JObject.Parse(data);
JToken jtkProduct;
jtkProduct = productsJson[dataName];
if (jtkProduct != null)
if (jtkProduct.HasValues)
{
int childrenCount = productsJson[dataName].Count();
for (int x = 0; x < childrenCount; x++)
ListOfItems.Add((T)Activator.CreateInstance(typeof(T), productsJson[dataName][x]));
}
}
return ListOfItems;
}
public static bool PropertiesEqual<T>(this T self, T other, string[] skip)
{
if (self.Equals(other)) return true;
var primitive = (from p in typeof(T).GetProperties()
where !skip.Contains(p.Name)
&& p.PropertyType.IsSimpleType()
select p).ToList();
var rest = (from p in typeof(T).GetProperties()
where !p.PropertyType.IsSimpleType() select p).ToList();
foreach(var pi in rest)
{
var selfValue = pi.GetValue(self, null);
var otherValue = pi.GetValue(other, null);
//var result = selfValue.PropertiesEqual(otherValue);
if (!object.Equals(selfValue, otherValue))
return false;
}
foreach (var pi in primitive)
{
var selfValue = pi.GetValue(self, null);
var otherValue = pi.GetValue(other, null);
return object.Equals(selfValue, otherValue);
}
return true;
}
public static bool IsSimpleType(this Type type)
{
return (type.IsValueType || type.IsPrimitive ||
type == typeof(String) || Convert.GetTypeCode(type) != TypeCode.Object);
}
I'm using this method to compare equality on my entity instances. On the first level it works great, but I would like to iterate over rest (attached entities) and do a recursive call to this method (the comment line).
The problem seems to be that self and other gets typed in to object on the recursive call, hence primitive gets zero results. If I inspect the type on self and this on the first level, I get the actual type, but on the second level I get object. I've tried using the Convert.ChangeType but it didn't help.
There's no reason to have it generic. Instead of typeof(T) use self.GetType() / other.GetType() to retrieve the correct run-time type.
What happens if you change the self and other value assignments to this?
var selfValue = Convert.ChangeType(pi.GetValue(self, null), pi.PropertyType);
var otherValue = Convert.ChangeType(pi.GetValue(other, null), pi.PropertyType);
I noticed that although T becomes object, self and other are of the correct type. So instead of gettign yoru propertes from typeof(T), try this:
var rest = (from p in self.GetType().GetProperties() select p).ToList();
In my simple test it worked and returned the wanted props.
You might have to change more than this one line :)
Way back, I did a comparerhelper and it allowed exclude properties.
Maybe code can help you:
public class CompareHelper
{
Hashtable reccorido = new Hashtable();
List<IExcludeProperties> excludeProperties = new List<IExcludeProperties>();
private readonly List<string> genericListPropertiesNames = new List<string>() { "Count", "Capacity", "Item" };
public CompareHelper():this(new List<IExcludeProperties>())
{
}
public CompareHelper(List<IExcludeProperties> excludeProperties)
{
this.excludeProperties = excludeProperties;
}
public bool AreEquals<T1, T2>(T1 value1, T2 value2)
{
try
{
reccorido = new Hashtable();
return Compare(value1, value2);
}
catch (NotEqualsException ex)
{
PropertyFail = ex.Where();
return false;
}
}
public string PropertyFail
{
get;
private set;
}
private bool Compare<T1, T2>(T1 value1, T2 value2)
{
if (value1 == null && value2 == null)
{
return true;
}
if ((value1 == null) || (value2 == null))
{
throw new NotEqualsException(value1, value2);
//return false;
}
string key = GetKey<T1, T2>(value1, value2);
if (reccorido.Contains(key))
{
return true;
}
reccorido.Add(key, true);
Type tipo1 = GetType(value1.GetType());
Type tipo2 = GetType(value2.GetType());
if (tipo1 != tipo2)
{
throw new NotEqualsException(value1, value2);
// return false;
}
if (IsBasicCompare(tipo1))
{
return CompareBasic(ConvertTo(value1, tipo1), ConvertTo(value2, tipo1));
}
dynamic v1 = ConvertTo(value1, tipo1);
dynamic v2 = ConvertTo(value2, tipo1);
if (!CompareFields(v1, v2))
{
throw new NotEqualsException(value1, value2);
//return false;
}
return CompareProperties(v1, v2);
}
private string GetKey<T1, T2>(T1 value1, T2 value2)
{
int hascodeA = value1.GetHashCode();
int hascodeB = value2.GetHashCode();
if (hascodeA > hascodeB)
return string.Format("{0}{1}", hascodeA, hascodeB);
return string.Format("{0}{1}", hascodeB, hascodeA);
}
private dynamic ConvertTo(object value1, Type t)
{
if (value1 == null)
return null;
return Convert.ChangeType(value1, GetType(t));
}
private bool CompareProperties<T>(T value1, T value2)
{
if (IsGenericList(typeof(T)))
{
return ComparareGenericList(value1, value2);
}
List<PropertyInfo> properties = GetPropertiesToCheck<T>();
foreach (var p in properties)
{
try
{
var valueA = p.GetValue(value1, null);
var valueB = p.GetValue(value2, null);
if (!(valueA == null && valueB == null))
{
if (valueA == null || valueB == null)
{
throw new NotEqualsException(value1, value2);
// return false;
}
if (IsBasicCompare(p.PropertyType))
{
valueA = ConvertTo(p.GetValue(value1, null), p.PropertyType);
valueB = ConvertTo(p.GetValue(value2, null), p.PropertyType);
if (!CompareBasic(valueA, valueB))
{
throw new NotEqualsException(value1, value2);
// return false;
}
}
else if (IsEnumerable(p.PropertyType))
{
if (!CompareAsInumerable(valueA, valueB))
{
throw new NotEqualsException(value1, value2);
// return false;
}
}
else if (p.PropertyType.IsClass)
{
if (!Compare(ConvertTo(p.GetValue(value1, null), p.PropertyType), ConvertTo(p.GetValue(value2, null), p.PropertyType)))
{
throw new NotEqualsException(value1, value2);
}
}
else
throw new Exception(string.Format("Tipo no especificado {0}", p.PropertyType));
}
}
catch (NotEqualsException ex)
{
ex.AddParent(p.Name);
throw;
}
}
return true;
}
private List<PropertyInfo> GetPropertiesToCheck<T>()
{
List<PropertyInfo> properties = new List<PropertyInfo>();
Type typeToCheck= typeof(T);
IExcludeProperties exclude=excludeProperties.FirstOrDefault(excl=>excl.ExcludeType().IsAssignableFrom(typeToCheck));
if(exclude!=null)
return typeToCheck.GetProperties().Where(p => p.CanRead && (!exclude.GetPropertiesNames().Any(n=>n==p.Name))).ToList();
//
return typeToCheck.GetProperties().Where(p => p.CanRead).ToList();
}
private bool ComparareGenericList<T>(T value1, T value2)
{
List<PropertyInfo> properties = typeof(T).GetProperties().Where(p => p.CanRead && p.Name != "Capacity").ToList(); //la capacidad no la compruebo!!
PropertyInfo count = typeof(T).GetProperty("Count");
int totalA = ConvertTo(count.GetValue(value1, null), count.PropertyType);
int totalB = ConvertTo(count.GetValue(value2, null), count.PropertyType);
if (!Compare(totalA, totalB))
return false;
PropertyInfo item = typeof(T).GetProperty("Item");
CompareAsInumerable(value1, value2);
return true;
}
private bool IsGenericList(Type t)
{
return t.IsGenericType && IsEnumerable(t) && t.GetProperties().Where(p => p.CanRead).Any(p => genericListPropertiesNames.Contains(p.Name));
}
[Conditional("DEBUG")]
private void ShowInfo(PropertyInfo p)
{
Debug.WriteLine(string.Format("Checkeando propiedad {0}",p.Name));
}
private bool CompareFields<T>(T value1, T value2)
{
List<FieldInfo> fields = typeof(T).GetFields().Where(f => f.IsPublic).ToList();
foreach (var f in fields)
{
dynamic valueA = f.GetValue(value1);
dynamic valueB = f.GetValue(value2);
if (!Compare(f.GetValue(value1), f.GetValue(value2)))
{
throw new NotEqualsException(value1, value2);
//return false;
}
}
return true;
}
private bool CompareAsInumerable<T>(T valueA, T valueB)
{
IEnumerable<object> colA = ((IEnumerable)valueA).Cast<object>();
IEnumerable<object> colB = ((IEnumerable)valueB).Cast<object>();
if (colA.Count() != colB.Count())
return false;
Type t1 = GetType(colA.GetType());
Type t2 = GetType(colB.GetType());
if (t1 != t2)
return false;
if (colA.Count() > 0)
{
Type itemType = GetTypeOfItem(colA);
for (int i = 0; i < colA.Count(); i++)
{
try
{
dynamic a = colA.ElementAt(i);
dynamic b = colB.ElementAt(i);
if (!Compare(a, b))
{
throw new NotEqualsException(colA.ElementAt(i), colB.ElementAt(i));
//return false;
}
}
catch (NotEqualsException ex)
{
ex.AddParent(itemType.Name);
throw ;
}
}
}
return true;
}
private Type GetTypeOfItem(IEnumerable<object> collection)
{
if (collection == null)
return null;
Type[] t = collection.GetType().GetGenericArguments();
if ((t != null) && (t.Count() > 0))
return t[0];
return null;
}
private bool IsEnumerable(Type type)
{
return typeof(IEnumerable).IsAssignableFrom(type);
}
private bool CompareBasic<T>(T valueA, T valueB)
{
bool result;
IComparable selfValueComparer;
selfValueComparer = valueA as IComparable;
if (valueA == null && valueB != null || valueA != null && valueB == null)
result = false;
else if (selfValueComparer != null && selfValueComparer.CompareTo(valueB) != 0)
result = false;
else if (!object.Equals(valueA, valueB))
result = false;
else
result = true;
if (!result)
throw new NotEqualsException(valueA, valueB);
return result;
}
private bool IsBasicCompare(Type type)
{
return typeof(IComparable).IsAssignableFrom(type) || type.IsPrimitive || type.IsValueType;
}
private Type GetType<T>()
{
return GetType(typeof(T));
}
private Type GetType(Type t)
{
Type tipo = Nullable.GetUnderlyingType(t);
if (tipo == null)
tipo = t;
return (tipo == null) ? t : tipo;
}
}
Helper class:
public interface IExcludeProperties
{
Type ExcludeType();
void AddPropertyName(string propertyName);
List<string> GetPropertiesNames();
}
public class ExcludeProperties<T> : IExcludeProperties
{
HashSet<string> propertiesNames = new HashSet<string>();
List<PropertyInfo> props = new List<PropertyInfo>();
public ExcludeProperties()
{
props = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly).ToList();
}
public Type ExcludeType()
{
return typeof(T);
}
public void AddPropertyName(string propertyName)
{
if(! typeof(T).IsAbstract && !props.Any(p=>p.Name==propertyName) )
throw new Exception(string.Format("No existe y no por lo tanto no se puede excluir la propiedad {0} para el tipo {1}!",propertyName,typeof(T).Name));
propertiesNames.Add(propertyName);
}
public List<string> GetPropertiesNames()
{
return propertiesNames.ToList();
}
}
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
Hi I'm using C++ to create a C# like dictionary object. I use a similar system of garbage collected ref objects for everything which are moved around in memory as the program runs. I've started off by implementing the dictionary aa a pretty standard hash table which is fine and I have this type of layout:
header + hash table -> storage -> element 0 -> object x
element 1 -> object y
element ... -> object ...
'+': In same allocation
'->': Indirect pointer ie different allocation
So 'header' contains just the table size. 'Hash table' is an array of integer offsets into the storage area.
The storage is implemented as a C# list ie an indirect pointer (ref object) to a self sizing array ie like a C++ vector.
Each element (Dictionary::Element) in the storage holds an id, an indirect pointer (ref object) to the actual object and an integer offset to the next element.
// C++ like pseudo code:
template< typename _Type_, typename _HashType_ = int >
class Dictionary
{
private:
class Element
{
_HashType_ m_id;
_Type_ m_object; // Ref object ie indirect pointer to actual object
int m_next; // Next element
}
int m_tablesize; // Power of 2
int* m_table; // Pointer here but in reality just a continuous block
// of memory after m_tablesize;
List<Element> m_storage; // Like a C++ vector
}
So my question is C#'s dictionary only allows one object at a time for any one hash.
Is there a simpler approach than the above implementation?
For example Dictonary::Add(_HashType_ id, _Type_ object) in the above implementation will bitwise AND the hash with the table size to get an index into the hash table then allocate an element with the id and object passed in and then it will add (push back) that element to the list (m_storage) of elements and fix up the linked list of elements:
template < typename _Type_, typename _HashType_ >
inline bool Dictionary< _Type_, _HashType_ >::Add( _HashType_ id, _Type_ element )
{
Element element = Element::New( id, object );
m_storage->Add( element );
// PushBack here fixes up the offset of the element in the storage array stored in
// the hash table (zero elements for this id) or the next pointer in the element
// (one or more elements exist for this id)
return PushBack( element );
}
Being a bit more explicit: is there a way to just have a header and hash table of objects ie:
header + hash table -> object x
object y
...
I ask this because C# imposes a one item limit on each hash when the more complex implementation above has no such limitations really except Remove would need to pass in both the id and the object and possibly you might want PushFront and PushBack instead of Add.
Thanks in advance and please don't ask why I'm doing this seemingly crazy thing just humor me! :)
You can use a decompiler to see how the dictionary is implemented in mscorlib. It is too long to include, but here is a snippet:
namespace System.Collections.Generic {
using System;
using System.Collections;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Runtime.Serialization;
using System.Security.Permissions;
[DebuggerTypeProxy(typeof(Mscorlib_DictionaryDebugView<,>))]
[DebuggerDisplay("Count = {Count}")]
[Serializable]
[System.Runtime.InteropServices.ComVisible(false)]
public class Dictionary<TKey,TValue>: IDictionary<TKey,TValue>, IDictionary, IReadOnlyDictionary<TKey, TValue>, ISerializable, IDeserializationCallback {
private struct Entry {
public int hashCode; // Lower 31 bits of hash code, -1 if unused
public int next; // Index of next entry, -1 if last
public TKey key; // Key of entry
public TValue value; // Value of entry
}
private int[] buckets;
private Entry[] entries;
private int count;
private int version;
private int freeList;
private int freeCount;
private IEqualityComparer<TKey> comparer;
private KeyCollection keys;
private ValueCollection values;
private Object _syncRoot;
// constants for serialization
private const String VersionName = "Version";
private const String HashSizeName = "HashSize"; // Must save buckets.Length
private const String KeyValuePairsName = "KeyValuePairs";
private const String ComparerName = "Comparer";
public Dictionary(): this(0, null) {}
public Dictionary(int capacity): this(capacity, null) {}
public Dictionary(IEqualityComparer<TKey> comparer): this(0, comparer) {}
public Dictionary(int capacity, IEqualityComparer<TKey> comparer) {
if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity);
if (capacity > 0) Initialize(capacity);
this.comparer = comparer ?? EqualityComparer<TKey>.Default;
}
public Dictionary(IDictionary<TKey,TValue> dictionary): this(dictionary, null) {}
public Dictionary(IDictionary<TKey,TValue> dictionary, IEqualityComparer<TKey> comparer):
this(dictionary != null? dictionary.Count: 0, comparer) {
if( dictionary == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
}
foreach (KeyValuePair<TKey,TValue> pair in dictionary) {
Add(pair.Key, pair.Value);
}
}
protected Dictionary(SerializationInfo info, StreamingContext context) {
//We can't do anything with the keys and values until the entire graph has been deserialized
//and we have a resonable estimate that GetHashCode is not going to fail. For the time being,
//we'll just cache this. The graph is not valid until OnDeserialization has been called.
HashHelpers.SerializationInfoTable.Add(this, info);
}
public IEqualityComparer<TKey> Comparer {
get {
return comparer;
}
}
public int Count {
get { return count - freeCount; }
}
public KeyCollection Keys {
get {
Contract.Ensures(Contract.Result<KeyCollection>() != null);
if (keys == null) keys = new KeyCollection(this);
return keys;
}
}
ICollection<TKey> IDictionary<TKey, TValue>.Keys {
get {
if (keys == null) keys = new KeyCollection(this);
return keys;
}
}
IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys {
get {
if (keys == null) keys = new KeyCollection(this);
return keys;
}
}
public ValueCollection Values {
get {
Contract.Ensures(Contract.Result<ValueCollection>() != null);
if (values == null) values = new ValueCollection(this);
return values;
}
}
ICollection<TValue> IDictionary<TKey, TValue>.Values {
get {
if (values == null) values = new ValueCollection(this);
return values;
}
}
IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values {
get {
if (values == null) values = new ValueCollection(this);
return values;
}
}
public TValue this[TKey key] {
get {
int i = FindEntry(key);
if (i >= 0) return entries[i].value;
ThrowHelper.ThrowKeyNotFoundException();
return default(TValue);
}
set {
Insert(key, value, false);
}
}
public void Add(TKey key, TValue value) {
Insert(key, value, true);
}
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> keyValuePair) {
Add(keyValuePair.Key, keyValuePair.Value);
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> keyValuePair) {
int i = FindEntry(keyValuePair.Key);
if( i >= 0 && EqualityComparer<TValue>.Default.Equals(entries[i].value, keyValuePair.Value)) {
return true;
}
return false;
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> keyValuePair) {
int i = FindEntry(keyValuePair.Key);
if( i >= 0 && EqualityComparer<TValue>.Default.Equals(entries[i].value, keyValuePair.Value)) {
Remove(keyValuePair.Key);
return true;
}
return false;
}
public void Clear() {
if (count > 0) {
for (int i = 0; i < buckets.Length; i++) buckets[i] = -1;
Array.Clear(entries, 0, count);
freeList = -1;
count = 0;
freeCount = 0;
version++;
}
}
public bool ContainsKey(TKey key) {
return FindEntry(key) >= 0;
}
public bool ContainsValue(TValue value) {
if (value == null) {
for (int i = 0; i < count; i++) {
if (entries[i].hashCode >= 0 && entries[i].value == null) return true;
}
}
else {
EqualityComparer<TValue> c = EqualityComparer<TValue>.Default;
for (int i = 0; i < count; i++) {
if (entries[i].hashCode >= 0 && c.Equals(entries[i].value, value)) return true;
}
}
return false;
}
private void CopyTo(KeyValuePair<TKey,TValue>[] array, int index) {
if (array == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
}
if (index < 0 || index > array.Length ) {
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
}
if (array.Length - index < Count) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
}
int count = this.count;
Entry[] entries = this.entries;
for (int i = 0; i < count; i++) {
if (entries[i].hashCode >= 0) {
array[index++] = new KeyValuePair<TKey,TValue>(entries[i].key, entries[i].value);
}
}
}
public Enumerator GetEnumerator() {
return new Enumerator(this, Enumerator.KeyValuePair);
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() {
return new Enumerator(this, Enumerator.KeyValuePair);
}
[System.Security.SecurityCritical] // auto-generated_required
public virtual void GetObjectData(SerializationInfo info, StreamingContext context) {
if (info==null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info);
}
info.AddValue(VersionName, version);
#if FEATURE_RANDOMIZED_STRING_HASHING
info.AddValue(ComparerName, HashHelpers.GetEqualityComparerForSerialization(comparer), typeof(IEqualityComparer<TKey>));
#else
info.AddValue(ComparerName, comparer, typeof(IEqualityComparer<TKey>));
#endif
info.AddValue(HashSizeName, buckets == null ? 0 : buckets.Length); //This is the length of the bucket array.
if( buckets != null) {
KeyValuePair<TKey, TValue>[] array = new KeyValuePair<TKey, TValue>[Count];
CopyTo(array, 0);
info.AddValue(KeyValuePairsName, array, typeof(KeyValuePair<TKey, TValue>[]));
}
}
private int FindEntry(TKey key) {
if( key == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
if (buckets != null) {
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next) {
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i;
}
}
return -1;
}
private void Initialize(int capacity) {
int size = HashHelpers.GetPrime(capacity);
buckets = new int[size];
for (int i = 0; i < buckets.Length; i++) buckets[i] = -1;
entries = new Entry[size];
freeList = -1;
}
private void Insert(TKey key, TValue value, bool add) {
if( key == null ) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
if (buckets == null) Initialize(0);
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
int targetBucket = hashCode % buckets.Length;
#if FEATURE_RANDOMIZED_STRING_HASHING
int collisionCount = 0;
#endif
for (int i = buckets[targetBucket]; i >= 0; i = entries[i].next) {
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) {
if (add) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
}
entries[i].value = value;
version++;
return;
}
#if FEATURE_RANDOMIZED_STRING_HASHING
collisionCount++;
#endif
}
int index;
if (freeCount > 0) {
index = freeList;
freeList = entries[index].next;
freeCount--;
}
else {
if (count == entries.Length)
{
Resize();
targetBucket = hashCode % buckets.Length;
}
index = count;
count++;
}
entries[index].hashCode = hashCode;
entries[index].next = buckets[targetBucket];
entries[index].key = key;
entries[index].value = value;
buckets[targetBucket] = index;
version++;
#if FEATURE_RANDOMIZED_STRING_HASHING
if(collisionCount > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(comparer))
{
comparer = (IEqualityComparer<TKey>) HashHelpers.GetRandomizedEqualityComparer(comparer);
Resize(entries.Length, true);
}
#endif
}
public virtual void OnDeserialization(Object sender) {
SerializationInfo siInfo;
HashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo);
if (siInfo==null) {
// It might be necessary to call OnDeserialization from a container if the container object also implements
// OnDeserialization. However, remoting will call OnDeserialization again.
// We can return immediately if this function is called twice.
// Note we set remove the serialization info from the table at the end of this method.
return;
}
int realVersion = siInfo.GetInt32(VersionName);
int hashsize = siInfo.GetInt32(HashSizeName);
comparer = (IEqualityComparer<TKey>)siInfo.GetValue(ComparerName, typeof(IEqualityComparer<TKey>));
if( hashsize != 0) {
buckets = new int[hashsize];
for (int i = 0; i < buckets.Length; i++) buckets[i] = -1;
entries = new Entry[hashsize];
freeList = -1;
KeyValuePair<TKey, TValue>[] array = (KeyValuePair<TKey, TValue>[])
siInfo.GetValue(KeyValuePairsName, typeof(KeyValuePair<TKey, TValue>[]));
if (array==null) {
ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MissingKeys);
}
for (int i=0; i<array.Length; i++) {
if ( array[i].Key == null) {
ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_NullKey);
}
Insert(array[i].Key, array[i].Value, true);
}
}
else {
buckets = null;
}
version = realVersion;
HashHelpers.SerializationInfoTable.Remove(this);
}
private void Resize() {
Resize(HashHelpers.ExpandPrime(count), false);
}
private void Resize(int newSize, bool forceNewHashCodes) {
Contract.Assert(newSize >= entries.Length);
int[] newBuckets = new int[newSize];
for (int i = 0; i < newBuckets.Length; i++) newBuckets[i] = -1;
Entry[] newEntries = new Entry[newSize];
Array.Copy(entries, 0, newEntries, 0, count);
if(forceNewHashCodes) {
for (int i = 0; i < count; i++) {
if(newEntries[i].hashCode != -1) {
newEntries[i].hashCode = (comparer.GetHashCode(newEntries[i].key) & 0x7FFFFFFF);
}
}
}
for (int i = 0; i < count; i++) {
int bucket = newEntries[i].hashCode % newSize;
newEntries[i].next = newBuckets[bucket];
newBuckets[bucket] = i;
}
buckets = newBuckets;
entries = newEntries;
}
public bool Remove(TKey key) {
if(key == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
if (buckets != null) {
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
int bucket = hashCode % buckets.Length;
int last = -1;
for (int i = buckets[bucket]; i >= 0; last = i, i = entries[i].next) {
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) {
if (last < 0) {
buckets[bucket] = entries[i].next;
}
else {
entries[last].next = entries[i].next;
}
entries[i].hashCode = -1;
entries[i].next = freeList;
entries[i].key = default(TKey);
entries[i].value = default(TValue);
freeList = i;
freeCount++;
version++;
return true;
}
}
}
return false;
}
public bool TryGetValue(TKey key, out TValue value) {
int i = FindEntry(key);
if (i >= 0) {
value = entries[i].value;
return true;
}
value = default(TValue);
return false;
}
// This is a convenience method for the internal callers that were converted from using Hashtable.
// Many were combining key doesn't exist and key exists but null value (for non-value types) checks.
// This allows them to continue getting that behavior with minimal code delta. This is basically
// TryGetValue without the out param
internal TValue GetValueOrDefault(TKey key) {
int i = FindEntry(key);
if (i >= 0) {
return entries[i].value;
}
return default(TValue);
}
bool ICollection<KeyValuePair<TKey,TValue>>.IsReadOnly {
get { return false; }
}
void ICollection<KeyValuePair<TKey,TValue>>.CopyTo(KeyValuePair<TKey,TValue>[] array, int index) {
CopyTo(array, index);
}
void ICollection.CopyTo(Array array, int index) {
if (array == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
}
if (array.Rank != 1) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
}
if( array.GetLowerBound(0) != 0 ) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound);
}
if (index < 0 || index > array.Length) {
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
}
if (array.Length - index < Count) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
}
KeyValuePair<TKey,TValue>[] pairs = array as KeyValuePair<TKey,TValue>[];
if (pairs != null) {
CopyTo(pairs, index);
}
else if( array is DictionaryEntry[]) {
DictionaryEntry[] dictEntryArray = array as DictionaryEntry[];
Entry[] entries = this.entries;
for (int i = 0; i < count; i++) {
if (entries[i].hashCode >= 0) {
dictEntryArray[index++] = new DictionaryEntry(entries[i].key, entries[i].value);
}
}
}
else {
object[] objects = array as object[];
if (objects == null) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
}
try {
int count = this.count;
Entry[] entries = this.entries;
for (int i = 0; i < count; i++) {
if (entries[i].hashCode >= 0) {
objects[index++] = new KeyValuePair<TKey,TValue>(entries[i].key, entries[i].value);
}
}
}
catch(ArrayTypeMismatchException) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
}
}
}
IEnumerator IEnumerable.GetEnumerator() {
return new Enumerator(this, Enumerator.KeyValuePair);
}
bool ICollection.IsSynchronized {
get { return false; }
}
object ICollection.SyncRoot {
get {
if( _syncRoot == null) {
System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
}
return _syncRoot;
}
}
bool IDictionary.IsFixedSize {
get { return false; }
}
bool IDictionary.IsReadOnly {
get { return false; }
}
< The rest of the code ommited...>
Its pretty similar to a std::map though a std::map doesn't require you to add an entry to it first ... you can add by just using the map's entry
ie.
std::map< int, int > m;
m[78]++;
will create and increment the int value represented by the key "78" ...
Is there an easy way in C# .NET to print out an ArrayList() "nicely" like how the WCF Test Client does:
...or was there a lot of looping and stuff going on in this program.
Again, can this be done easily or not? Not asking how if its complicated.
Also, just plain text is fine those items are interactable
You can use this object dumper class from the Linq Samples, or you could use the JSON serializer.
//Copyright (C) Microsoft Corporation. All rights reserved.
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
// See the ReadMe.html for additional information
public class ObjectDumper {
public static void Write(object element)
{
Write(element, 0);
}
public static void Write(object element, int depth)
{
Write(element, depth, Console.Out);
}
public static void Write(object element, int depth, TextWriter log)
{
ObjectDumper dumper = new ObjectDumper(depth);
dumper.writer = log;
dumper.WriteObject(null, element);
}
TextWriter writer;
int pos;
int level;
int depth;
private ObjectDumper(int depth)
{
this.depth = depth;
}
private void Write(string s)
{
if (s != null) {
writer.Write(s);
pos += s.Length;
}
}
private void WriteIndent()
{
for (int i = 0; i < level; i++) writer.Write(" ");
}
private void WriteLine()
{
writer.WriteLine();
pos = 0;
}
private void WriteTab()
{
Write(" ");
while (pos % 8 != 0) Write(" ");
}
private void WriteObject(string prefix, object element)
{
if (element == null || element is ValueType || element is string) {
WriteIndent();
Write(prefix);
WriteValue(element);
WriteLine();
}
else {
IEnumerable enumerableElement = element as IEnumerable;
if (enumerableElement != null) {
foreach (object item in enumerableElement) {
if (item is IEnumerable && !(item is string)) {
WriteIndent();
Write(prefix);
Write("...");
WriteLine();
if (level < depth) {
level++;
WriteObject(prefix, item);
level--;
}
}
else {
WriteObject(prefix, item);
}
}
}
else {
MemberInfo[] members = element.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance);
WriteIndent();
Write(prefix);
bool propWritten = false;
foreach (MemberInfo m in members) {
FieldInfo f = m as FieldInfo;
PropertyInfo p = m as PropertyInfo;
if (f != null || p != null) {
if (propWritten) {
WriteTab();
}
else {
propWritten = true;
}
Write(m.Name);
Write("=");
Type t = f != null ? f.FieldType : p.PropertyType;
if (t.IsValueType || t == typeof(string)) {
WriteValue(f != null ? f.GetValue(element) : p.GetValue(element, null));
}
else {
if (typeof(IEnumerable).IsAssignableFrom(t)) {
Write("...");
}
else {
Write("{ }");
}
}
}
}
if (propWritten) WriteLine();
if (level < depth) {
foreach (MemberInfo m in members) {
FieldInfo f = m as FieldInfo;
PropertyInfo p = m as PropertyInfo;
if (f != null || p != null) {
Type t = f != null ? f.FieldType : p.PropertyType;
if (!(t.IsValueType || t == typeof(string))) {
object value = f != null ? f.GetValue(element) : p.GetValue(element, null);
if (value != null) {
level++;
WriteObject(m.Name + ": ", value);
level--;
}
}
}
}
}
}
}
}
private void WriteValue(object o)
{
if (o == null) {
Write("null");
}
else if (o is DateTime) {
Write(((DateTime)o).ToShortDateString());
}
else if (o is ValueType || o is string) {
Write(o.ToString());
}
else if (o is IEnumerable) {
Write("...");
}
else {
Write("{ }");
}
}
}
i think you might be looking for the propertygrid