PCB MS SQL 儲存過程(CLR) 實現Json轉DataTable表的方法
阿新 • • 發佈:2018-12-15
一.準備需轉為DataTable的json字串
原json字串資料
[{"TechName":"ECN","TechNo":"EC_01","id":"1"},{"TechName":"流程指示","TechNo":"PI_01","id":"2"},{"TechName":"計劃投料","TechNo":"FP_01","id":"3"},{"TechName":"開料","TechNo":"CC_01","id":"4"},{"TechName":"裁切","TechNo":"CC_02","id":"5"},{"TechName":"磨邊","TechNo":"CC_03","id":"6"},{"TechName":"圓角","TechNo":"CC_04","id":"7"},{"TechName":"打標","TechNo":"CC_05","id":"8"},{"TechName":"烘板","TechNo":"CC_06","id":"9"},{"TechName":"鑽孔","TechNo":"MD_02","id":"10"},{"TechName":"去毛刺","TechNo":"PT_01","id":"11"},{"TechName":"烘板","TechNo":"PT_05","id":"12"},{"TechName":"等離子處理","TechNo":"PT_02","id":"13"},{"TechName":"沉銅","TechNo":"PT_07","id":"14"},{"TechName":"負片電鍍","TechNo":"PP_02","id":"15"},{"TechName":"磨板","TechNo":"PP_05","id":"16"},{"TechName":"外層前處理","TechNo":"OL_01","id":"17"},{"TechName":"貼膜","TechNo":"OL_03","id":"18"},{"TechName":"曝光","TechNo":"OL_04","id":"19"},{"TechName":"顯影","TechNo":"OL_05","id":"20"},{"TechName":"酸性蝕刻","TechNo":"OL_06","id":"21"},{"TechName":"退膜","TechNo":"OL_07","id":"22"},{"TechName":"外層AOI","TechNo":"AO_04","id":"23"},{"TechName":"烘板","TechNo":"SM_02","id":"24"},{"TechName":"阻焊前處理","TechNo":"SM_01","id":"25"},{"TechName":"絲印","TechNo":"SM_05","id":"26"},{"TechName":"阻焊前處理","TechNo":"SM_01","id":"27"},{"TechName":"絲印","TechNo":"SM_05","id":"28"},{"TechName":"預烘","TechNo":"SM_06","id":"29"},{"TechName":"曝光","TechNo":"SM_09","id":"30"},{"TechName":"顯影","TechNo":"SM_10","id":"31"},{"TechName":"終固化","TechNo":"SM_11","id":"32"},{"TechName":"噴砂","TechNo":"EN_01","id":"33"},{"TechName":"板邊包膠","TechNo":"EN_02","id":"34"},{"TechName":"沉金","TechNo":"EN_04","id":"35"},{"TechName":"水洗烘乾","TechNo":"EN_06","id":"36"},{"TechName":"銑板","TechNo":"FO_02","id":"37"},{"TechName":"成品清洗","TechNo":"FO_14","id":"38"},{"TechName":"小板切割","TechNo":"FO_08","id":"39"},{"TechName":"功能檢查","TechNo":"FI_01","id":"40"},{"TechName":"外觀檢查","TechNo":"FI_02","id":"41"},{"TechName":"內包裝","TechNo":"PK_01","id":"42"},{"TechName":"入庫","TechNo":"DE_01","id":"43"},{"TechName":"外包裝","TechNo":"DE_02","id":"44"},{"TechName":"出庫","TechNo":"DE_03","id":"45"}] '
轉換後的效果(橫向)
轉換後的效果(豎向)
二.C#寫SQL SERVER(CLR) json轉DataTable儲存過程
利用Json反序列化為字典型別 Dictionary<string, object> key,value形式,然後再轉為DataTable
/// <summary> /// Json轉Table /// </summary> /// <param name="jsonString"></param> /// <param name="isVerticalTable">是否豎表</param> [Microsoft.SqlServer.Server.SqlProcedure] public static void Json2Table(string jsonString, boolView CodeisVerticalTable) { SqlDataRecord dataRecord = new SqlDataRecord(); var ListObject = Json.Deserialize(jsonString) as List<object>; var Object = Json.Deserialize(jsonString) as Dictionary<string, object>; if (ListObject == null && Object == null) return; if (ListObject == null) { ListObject = new List<object>(); ListObject.Add(Object); } List<SqlMetaData> sqlMetaDatas = new List<SqlMetaData>(); Dictionary<string, object> KeyObject = (Dictionary<string, object>)ListObject[0]; if (!isVerticalTable) { foreach (var jProperty in KeyObject) { sqlMetaDatas.Add(new SqlMetaData(jProperty.Key, SqlDbType.NVarChar, 4000)); } dataRecord = new SqlDataRecord(sqlMetaDatas.ToArray()); SqlContext.Pipe.SendResultsStart(dataRecord); for (int i = 0; i < ListObject.Count(); i++) { Dictionary<string, object> DicObject = (Dictionary<string, object>)ListObject[i]; int j = 0; foreach (var item in KeyObject) { dataRecord.SetString(j, DicObject[item.Key].ToStringJson()); j++; } SqlContext.Pipe.SendResultsRow(dataRecord); } SqlContext.Pipe.SendResultsEnd(); } else { dataRecord = new SqlDataRecord(new SqlMetaData[] { new SqlMetaData("id", SqlDbType.Int), new SqlMetaData("key", SqlDbType.NVarChar, 4000), new SqlMetaData("value", SqlDbType.NVarChar, 4000) }); SqlContext.Pipe.SendResultsStart(dataRecord); for (int i = 0; i < ListObject.Count(); i++) { Dictionary<string, object> DicObject = (Dictionary<string, object>)ListObject[i]; foreach (var item in DicObject) { dataRecord.SetInt32(0, i + 1); dataRecord.SetString(1, item.Key.ToString()); dataRecord.SetString(2, item.Value.ToStringJson()); SqlContext.Pipe.SendResultsRow(dataRecord); } } SqlContext.Pipe.SendResultsEnd(); } SqlContext.Pipe.Send("ok"); }
開源MiniJSON類解析Json字串
using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; namespace SQLClr { /// <summary> /// This class encodes and decodes JSON strings. /// Spec. details, see http://www.json.org/ /// /// JSON uses Arrays and Objects. These correspond here to the datatypes IList and IDictionary. /// All numbers are parsed to doubles. /// </summary> public static class Json { /// <summary> /// Parses the string json into a value /// </summary> /// <param name="json">A JSON string.</param> /// <returns>An List<object>, a Dictionary<string, object>, a double, an integer,a string, null, true, or false</returns> //反序列化 public static object Deserialize(string json) { // save the string for debug information if (json == null) { return null; } return Parser.Parse(json); } //阻止其他類從該類繼承 sealed class Parser : IDisposable { const string WORD_BREAK = "{}[],:\""; public static bool IsWordBreak(char c) { // 如果 c 是空白,則為 true;否則,為 false;報告指定 Unicode 字元在此字串中的第一個匹配項的索引。 return Char.IsWhiteSpace(c) || WORD_BREAK.IndexOf(c) != -1; } enum TOKEN { NONE, CURLY_OPEN, CURLY_CLOSE, SQUARED_OPEN, SQUARED_CLOSE, COLON, COMMA, STRING, NUMBER, TRUE, FALSE, NULL }; // 實現從字串進行讀取的 System.IO.TextReader。 StringReader json; Parser(string jsonString) { json = new StringReader(jsonString); } public static object Parse(string jsonString) { using (var instance = new Parser(jsonString)) { return instance.ParseValue(); } } //釋放 public void Dispose() { json.Dispose(); json = null; } Dictionary<string, object> ParseObject() { Dictionary<string, object> table = new Dictionary<string, object>(); // ditch opening brace json.Read(); // { while (true) { switch (NextToken) { case TOKEN.NONE: return null; case TOKEN.COMMA: continue; case TOKEN.CURLY_CLOSE: return table; default: // name string name = ParseString(); if (name == null) { return null; } // : if (NextToken != TOKEN.COLON) { return null; } // ditch the colon json.Read(); // value table[name] = ParseValue(); break; } } } List<object> ParseArray() { List<object> array = new List<object>(); // ditch opening bracket json.Read(); // [ bool parsing = true; while (parsing) { TOKEN nextToken = NextToken; switch (nextToken) { case TOKEN.NONE: return null; case TOKEN.COMMA: continue; case TOKEN.SQUARED_CLOSE: parsing = false; break; default: object value = ParseByToken(nextToken); array.Add(value); break; } } return array; } object ParseValue() { TOKEN nextToken = NextToken; return ParseByToken(nextToken); } object ParseByToken(TOKEN token) { switch (token) { case TOKEN.STRING: return ParseString(); case TOKEN.NUMBER: return ParseNumber(); case TOKEN.CURLY_OPEN: return ParseObject(); case TOKEN.SQUARED_OPEN: return ParseArray(); case TOKEN.TRUE: return true; case TOKEN.FALSE: return false; case TOKEN.NULL: return null; default: return null; } } string ParseString() { StringBuilder s = new StringBuilder(); char c; // ditch opening quote json.Read(); bool parsing = true; while (parsing) { if (json.Peek() == -1) { parsing = false; break; } c = NextChar; switch (c) { case '"': parsing = false; break; case '\\': if (json.Peek() == -1) { parsing = false; break; } c = NextChar; switch (c) { case '"': case '\\': case '/': s.Append(c); break; case 'b': s.Append('\b'); break; case 'f': s.Append('\f'); break; case 'n': s.Append('\n'); break; case 'r': s.Append('\r'); break; case 't': s.Append('\t'); break; case 'u': var hex = new char[4]; for (int i = 0; i < 4; i++) { hex[i] = NextChar; } s.Append((char)Convert.ToInt32(new string(hex), 16)); break; } break; default: s.Append(c); break; } } return s.ToString(); } object ParseNumber() { string number = NextWord; // 摘要: // 報告指定 Unicode 字元在此字串中的第一個匹配項的索引。 // // 引數: // value: // 要查詢的 Unicode 字元。 // // 返回結果: // 如果找到該字元,則為 value 的從零開始的索引位置;如果未找到,則為 -1。 if (number.IndexOf('.') == -1) { long parsedInt; // 將數字的字串表示形式轉換為它的等效 64 位有符號整數。一個指示轉換是否成功的返回值。 Int64.TryParse(number, out parsedInt); return parsedInt; } double parsedDouble; Double.TryParse(number, out parsedDouble); return parsedDouble; } // void EatWhitespace() { //指示指定字串中位於指定位置處的字元是否屬於空白類別。 while (Char.IsWhiteSpace(PeekChar)) { json.Read(); //摘要: // 返回下一個可用的字元,但不使用它。 // // 返回結果: // 表示下一個要讀取的字元的整數,或者,如果沒有更多的可用字元或該流不支援查詢,則為 -1。 if (json.Peek() == -1) { break; } } } char PeekChar { get { // 讀取輸入字串中的下一個字元並將該字元的位置提升一個字元。 // // 返回結果: // 基礎字串中的下一個字元,或者如果沒有更多的可用字元,則為 -1。 return Convert.ToChar(json.Peek()); } } char NextChar { get { return Convert.ToChar(json.Read()); } } string NextWord { get { // 表示可變字元字串。無法繼承此類。 StringBuilder word = new StringBuilder(); while (!IsWordBreak(PeekChar)) { // 摘要: // 在此例項的結尾追加指定 Unicode 字元的字串表示形式。 // // 引數: // value: // 要追加的 Unicode 字元。 // // 返回結果: // 完成追加操作後對此例項的引用。 word.Append(NextChar); //下一個字元為空 if (json.Peek() == -1) { break; } } // return word.ToString(); } } TOKEN NextToken { get { EatWhitespace(); if (json.Peek() == -1) { return TOKEN.NONE; } switch (PeekChar) { case '{': return TOKEN.CURLY_OPEN; case '}': json.Read(); return TOKEN.CURLY_CLOSE; case '[': return TOKEN.SQUARED_OPEN; case ']': json.Read(); return TOKEN.SQUARED_CLOSE; case ',': json.Read(); return TOKEN.COMMA; case '"': return TOKEN.STRING; case ':': return TOKEN.COLON; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': return TOKEN.NUMBER; } switch (NextWord) { case "false": return TOKEN.FALSE; case "true": return TOKEN.TRUE; case "null": return TOKEN.NULL; } return TOKEN.NONE; } } } /// <summary> /// Converts a IDictionary / IList object or a simple type (string, int, etc.) into a JSON string /// </summary> /// <param name="json">A Dictionary<string, object> / List<object></param> /// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns> public static string Serialize(object obj) { return Serializer.Serialize(obj); } sealed class Serializer { StringBuilder builder; Serializer() { //建立生成器 builder = new StringBuilder(); } //序列化 public static string Serialize(object obj) { var instance = new Serializer(); instance.SerializeValue(obj); return instance.builder.ToString(); } //型別 void SerializeValue(object value) { IList asList; IDictionary asDict; string asStr; if (value == null) { builder.Append("null"); } else if ((asStr = value as string) != null) { SerializeString(asStr); } else if (value is bool) { builder.Append((bool)value ? "true" : "false"); } else if ((asList = value as IList) != null) { SerializeArray(asList); } else if ((asDict = value as IDictionary) != null) { SerializeObject(asDict); } else if (value is char) { SerializeString(new string((char)value, 1)); } else { SerializeOther(value); } } //序列化物件 void SerializeObject(IDictionary obj) { bool first = true; builder.Append('{'); foreach (object e in obj.Keys) { if (!first) { builder.Append(','); } SerializeString(e.ToString()); builder.Append(':'); SerializeValue(obj[e]); first = false; } builder.Append('}'); } // 序列化陣列 void SerializeArray(IList anArray) { builder.Append('['); bool first = true; foreach (object obj in anArray) { if (!first) { builder.Append(','); } SerializeValue(obj); first = false; } builder.Append(']'); } //string void SerializeString(string str) { builder.Append('\"'); char[] charArray = str.ToCharArray(); foreach (var c in charArray) { switch (c) { case '"': builder.Append("\\\""); break; case '\\': builder.Append("\\\\"); break; case '\b': builder.Append("\\b"); break; case '\f': builder.Append("\\f"); break; case '\n': builder.Append("\\n"); break; case '\r': builder.Append("\\r"); break; case '\t': builder.Append("\\t"); break; default: int codepoint = Convert.ToInt32(c); if ((codepoint >= 32) && (codepoint <= 126)) { builder.Append(c); } else { //builder.Append("\\u"); //builder.Append(codepoint.ToString("x4")); builder.Append(c); } break; } } builder.Append('\"'); } //其他 void SerializeOther(object value) { // NOTE: decimals lose precision during serialization. // They always have, I'm just letting you know. // Previously floats and doubles lost precision too. //注意:小數在序列化過程中丟失精度。 //他們總是有,我只是讓你知道。 //以前失去精度和雙精度浮點數。 if (value is float) { builder.Append(((float)value).ToString("R")); } else if (value is int || value is uint || value is long || value is sbyte || value is byte || value is short || value is ushort || value is ulong) { builder.Append(value); } else if (value is double || value is decimal) { builder.Append(Convert.ToDouble(value).ToString("R")); } else { SerializeString(value.ToString()); } } } } }View Code
三.SQL伺服器CLR配置(允許SQL呼叫.net程式)
sp_configure 'show advanced options', 1; RECONFIGURE WITH override GO sp_configure 'clr enabled', 1; RECONFIGURE WITH override GO Sp_changedbowner 'sa',true --sa改為當前登入使用者名稱 alter database [dbname] set trustworthy on --bbname 改為自己的庫名
四.註冊 CLR 程式集
create ASSEMBLY SQLfunctionAssembly FROM 'D:\SQLClr.dll' --改為自己C#寫的dll路徑填寫 WITH PERMISSION_SET = UNSAFE;
建立的.net程式集資料會寫入下表:
select * from sys.assemblies select * from sys.assembly_files
五.建立儲存過程
CREATE PROCEDURE[dbo].[Json2Table] ( @jsonString NVARCHAR(max), @isVerticalTable bit ) WITH EXECUTE AS CALLER AS EXTERNAL NAME [SQLfunctionAssembly].[SQLClr.SQLfunction].[Json2Table] --[SQL程式集名].[名稱空間.類名].[方法名]
六.測試Json轉DataTable儲存過程
DECLARE @jsonString VARCHAR(MAX) SET @jsonString = ' [{"TechName":"ECN","TechNo":"EC_01","id":"1"},{"TechName":"流程指示","TechNo":"PI_01","id":"2"},{"TechName":"計劃投料","TechNo":"FP_01","id":"3"},{"TechName":"開料","TechNo":"CC_01","id":"4"},{"TechName":"裁切","TechNo":"CC_02","id":"5"},{"TechName":"磨邊","TechNo":"CC_03","id":"6"},{"TechName":"圓角","TechNo":"CC_04","id":"7"},{"TechName":"打標","TechNo":"CC_05","id":"8"},{"TechName":"烘板","TechNo":"CC_06","id":"9"},{"TechName":"鑽孔","TechNo":"MD_02","id":"10"},{"TechName":"去毛刺","TechNo":"PT_01","id":"11"},{"TechName":"烘板","TechNo":"PT_05","id":"12"},{"TechName":"等離子處理","TechNo":"PT_02","id":"13"},{"TechName":"沉銅","TechNo":"PT_07","id":"14"},{"TechName":"負片電鍍","TechNo":"PP_02","id":"15"},{"TechName":"磨板","TechNo":"PP_05","id":"16"},{"TechName":"外層前處理","TechNo":"OL_01","id":"17"},{"TechName":"貼膜","TechNo":"OL_03","id":"18"},{"TechName":"曝光","TechNo":"OL_04","id":"19"},{"TechName":"顯影","TechNo":"OL_05","id":"20"},{"TechName":"酸性蝕刻","TechNo":"OL_06","id":"21"},{"TechName":"退膜","TechNo":"OL_07","id":"22"},{"TechName":"外層AOI","TechNo":"AO_04","id":"23"},{"TechName":"烘板","TechNo":"SM_02","id":"24"},{"TechName":"阻焊前處理","TechNo":"SM_01","id":"25"},{"TechName":"絲印","TechNo":"SM_05","id":"26"},{"TechName":"阻焊前處理","TechNo":"SM_01","id":"27"},{"TechName":"絲印","TechNo":"SM_05","id":"28"},{"TechName":"預烘","TechNo":"SM_06","id":"29"},{"TechName":"曝光","TechNo":"SM_09","id":"30"},{"TechName":"顯影","TechNo":"SM_10","id":"31"},{"TechName":"終固化","TechNo":"SM_11","id":"32"},{"TechName":"噴砂","TechNo":"EN_01","id":"33"},{"TechName":"板邊包膠","TechNo":"EN_02","id":"34"},{"TechName":"沉金","TechNo":"EN_04","id":"35"},{"TechName":"水洗烘乾","TechNo":"EN_06","id":"36"},{"TechName":"銑板","TechNo":"FO_02","id":"37"},{"TechName":"成品清洗","TechNo":"FO_14","id":"38"},{"TechName":"小板切割","TechNo":"FO_08","id":"39"},{"TechName":"功能檢查","TechNo":"FI_01","id":"40"},{"TechName":"外觀檢查","TechNo":"FI_02","id":"41"},{"TechName":"內包裝","TechNo":"PK_01","id":"42"},{"TechName":"入庫","TechNo":"DE_01","id":"43"},{"TechName":"外包裝","TechNo":"DE_02","id":"44"},{"TechName":"出庫","TechNo":"DE_03","id":"45"}] ' exec dbo.Json2Table @jsonString,0 --0轉橫表 1轉豎表
執行後結果(轉橫表)
執行後結果(轉豎表)