我在一个没有使用第三方库的灵活性的平台上使用C#3.5的缩减版本。
虽然我可以解析JSON(使用json流阅读器),但我不确定如何将其转换为类。 (也没有通常的json访问类反序列化器。)
有谁知道如何使用反射手动(但动态地)将JSON字符串转换为类?
示例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"}]}
]}
答案 0 :(得分:1)
好的,我正在根据反馈重做我的回答。动态对象生成器代码仍然来自:
Deserialize JSON into C# dynamic object?
这使用RegEx,Generic集合,它确实使用Linq,但只有2行,并且可以很容易地重写那些不使用Linq(DynamicJsonObject.TryGetMember()
末尾的两个'result ='行)。如有必要,通用字典也可以用哈希表替换。
json解析器改编自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))));
}
}
答案 1 :(得分:0)
再次感谢Pete和其他人的精彩帖子。我已经把我的SQL Server CLR标量函数包裹起来,这在查询存储在关系表中的JSON时非常有用(我知道有人会说只使用MongoDB!)。
请参阅以下内容:
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("]");
}
}
CLR功能如下:
/// <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);
}
用法是:
-- 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');