簡易的Json轉換的實現


環境

數據庫字段中保存着Json數據,用於保存用戶的權限,這些Json數據,不需要數據庫去處理。 這似乎是一個生命中常見的命題,本來不可能,卻非有人要打破它。

菜單表是自增ID

權限字如下,表示角色擁有的頁面權限,按鈕權限,行集權限,行集權限包括 查看權限,修改權限,刪除權限。 查看權限描述了可以查看 哪些表的 哪些行。 其中 表的哪些行是用一個大數字來保存的。

{Action:"0",Button:"0",Row:{View:{Menu:"F,FFC00000,0,0,3E0004"},Edit:{},Delete:{},IsMax:false},IsMax:false}

 

其中Menu后面的一大串是大數字。逗號分隔的每個部分是一個uint , 表示在該2進制位上是否擁有該菜單 。 如  5 表示角色擁有 第1行 和第3 行菜單 。 5 的二進制編碼是  101  = 1 * 2^2 + 0 * 2^1 + 1 * 2^0 , 即: 第一行和第三行。

為了描述簡單,把從權限字中計算得到的菜單表的行集稱為 權限行集,

遇到的問題

設計人員提出:用腳本設置角色的權限。如,給所有角色添加一個菜單權限。

該功能在程序端的實現方式是,對權限字反序列化到對象上,把大數字取出,進行位運算,取出權限行集,與設置菜單ID 進行合並(增加一行或刪除一行)。

如果在數據庫上實現該功能,最好還是用.Net 來完成。

在數據庫端的實現

在Sqlserver 2008 + 上,可以編寫.net 程序集對sqlserver擴展, 好像java也可以對oracle 進行擴展。

最初的想法是 在數據庫上引用 Json.Net ,再創建一個自定義程序集,自定義程序集引用數據庫的Json.Net 。 但數據庫上的程序集有諸多條件: http://msdn.microsoft.com/en-us/library/ms189524.aspx , 最典型的是 static 必須是 readonly 的。我把Json.Net 2.0 的程序集按要求改了之后,注入還是出錯: 收集元數據時出錯 。所以只能再找辦法。

由於Json是比較簡單的形式,所以決定自己寫一個 Json 的反序列化。

過程比較簡單: 建一個 C# CLR 數據庫項目。

確定以下規則:

1. 反斜線是轉義,反斜線后面的字符可忽略規則。
2. 引號是整體
3. 冒號分詞
4. { } , [] 算是一個整體 可以無限級。

編寫的方式要簡單,原始。輸入參數:JSON,KEY , 返回 KEY 后表示的Value 字符串。

代碼如下:

    [SqlFunction]
    public static SqlString GetJsonValue(string Value, string Key)
    {
        //返回一個string 數組,這個數組符合IEnumerable接口,當然你也可以返回hashtable等類型。
        Value = Value.Trim();
        if (!Value.StartsWith("{") || !Value.EndsWith("}")) throw new Exception("非法Json");


        /*
         * 規則:
         * 1. 反斜線是轉義,反斜線后面的字符可忽略規則。
         * 2. 引號是整體
         * 3. 冒號分詞
         * 4. { } 算是一個整體 可以無限級。
         */
        Value = Value.Substring(1, Value.Length - 2);

        for (var i = 0; i < Value.Length; i++)
        {
            int keyEndIndex = PowerJson.FindNext(Value, i, ':');

            var key = Value.Substring(i, keyEndIndex - i).Trim();

            var valueEndIndex = PowerJson.FindNext(Value, keyEndIndex + 1, ',');

            i = valueEndIndex;

            var val = Value.Substring(keyEndIndex + 1, valueEndIndex - keyEndIndex - 1).Trim();


            if (key.StartsWith(@"""") && key.EndsWith(@"""")) key = key.Substring(1, key.Length - 2);
            if (val.StartsWith(@"""") && val.EndsWith(@"""")) val = val.Substring(1, val.Length - 2);

            if (string.Equals(key, Key, StringComparison.CurrentCultureIgnoreCase)) return val;
        }

        return string.Empty;
    }

PowerJson 的分詞函數:

        public static int FindNext(string Value, int pos, char findChar)
        {
            /*
             * 規則:
             * 1. 反斜線是轉義,反斜線后面的字符可忽略規則。
             * 2. 雙引號是整體,單引號是整體
             * 3. {} 是整體,[] 是整體。
             * 4. 冒號分詞
             */

            //結束
            if (pos == Value.Length) return Value.Length;
            int ClsLevel = 0;
            int AryLevel = 0;

            bool inQuote1 = false;
            bool inQuote2 = false;

            for (int i = pos; i < Value.Length; i++)
            {
                var item = Value[i];
                if (item == '\\')
                {
                    i++;
                    continue;
                }

                if (ClsLevel == 0 && AryLevel == 0 && inQuote1 == false && inQuote2 == false && findChar == item) return i;

                if (inQuote1)
                {
                    if (item == '\'')
                    {
                        inQuote1 = !inQuote1;
                    }

                    continue;
                }

                if (inQuote2)
                {
                    if (item == '"')
                    {
                        inQuote2 = !inQuote2;
                    }

                    continue;
                }


                if (inQuote1 == false && inQuote2 == false)
                {
                    if (item == '\'')
                    {
                        inQuote1 = true;
                        continue;
                    }

                    if (item == '"')
                    {
                        inQuote2 = true;
                        continue;
                    }
                }

                if (item == '{')
                {
                    ClsLevel++;
                    continue;
                }

                if (item == '}')
                {
                    ClsLevel--;
                    continue;
                }

                if (item == '[')
                {
                    AryLevel++;
                    continue;
                }

                if (item == ']')
                {
                    AryLevel--;
                    continue;
                }
            }

            return Value.Length;
        }

 

使用SQL把程序集注入:

---
exec sp_configure 'show advanced options', '1';
go
reconfigure;
go
exec sp_configure 'clr enabled', '1'
go
reconfigure;
exec sp_configure 'show advanced options', '1';
go

CREATE ASSEMBLY MyCLr 
FROM   'G:\共享\個人共享\Udi\MyClr\MyClr.dll'
WITH permission_set = Safe;
GO

CREATE FUNCTION [dbo].[GetJsonValue](@val [nvarchar](4000), @key nvarchar(200))
RETURNS [nvarchar](4000) WITH EXECUTE AS CALLER
AS 
EXTERNAL NAME [MyClr].[MyClr].[GetJsonValue]
go

 

在數據庫端進行測試,取出Row.View.Menu的值:

select dbo.GetJsonValue( dbo.GetJsonValue( dbo.GetJsonValue( [Power],'Row'),'View'),'Menu')  from [Role]

得到的是大數字。

后續的大數字計算,由於SQL server 程序集只能使用 .net 3.5 ,所以 .Net 4.0 的大數字System.Numerics.BigInteger 就不能使用了,可以參考開源的,如下:

http://bignumber.codeplex.com/

http://www.codeproject.com/Articles/36323/BigInt

 

我在BigInt 的基礎上稍做修改,主要是格式化輸出,和對格式化輸出進行解析。 有了開源的實現,這就容易多了。

實現之后感覺反序列Json還是非常簡單的。在輕量級應用上,非常方便。

經測試,性能還不錯。

 

JOSN轉義問題

對象 =》 JSON 字符串 ,需要把 真回車"\n" 轉換為 字符串 "\\n"

反之

Json字符串 =》 對象,需要把字符串中的回車 "\\n" 轉換為 "\n"

 

要處理的字符包括:

\\r  => \r

\\n  => \n

\\t  => \t

\\"  => \"

\\'  => \'

最后處理

\\\\ => \\

\\u0026 => &  等特殊字符。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM