2013-02-19 16 views
1

我正在使用第三方庫的靈活性不能使用第三方庫的平臺上的C#3.5的減少版本。反序列化json到類(手動反射)

雖然我可以解析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

請問[DataContractJsonSeriazer(HTTP:// MSDN。 microsoft.com/en-us/library/system.runtime.serialization.json.datacontractjsonserializer(v=vs.90).aspx)也沒有選擇? – nemesv 2013-02-19 21:20:19

+0

不幸的是,這不是一個選項 – 2013-02-19 21:59:12

回答

1

好吧,我根據反饋重做我的答案。動態對象生成的代碼仍然來源於此:

Deserialize JSON into C# dynamic object?

它使用正則表達式,泛型集合,它並使用Linq,但只有在2線和那些可以輕易改寫爲不使用Linq(兩個「結果='在DynamicJsonObject.TryGetMember()末尾的行)。如果需要,通用字典也可以用散列表替換。

的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)))); 
    } 
} 
+0

嘿皮特。 感謝您的回答。 不幸的是,沒有JavaScript轉換器,我不認爲我可以使用內部/密封類(由於系統的限制) – 2013-02-19 21:43:08

+0

這是否正確地轉義引用字符? – bmm6o 2013-02-19 21:45:33

+0

JavaScriptConverter:http://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptconverter.aspx 至於它被「內部密封」更改爲公開並刪除密封關鍵字。 – Pete 2013-02-19 21:46:38

0

感謝皮特和其他人對他們的輝煌後。我已經圍繞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');