• 简易的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 => &  等特殊字符。

    alarm   作者:NewSea     出处:http://newsea.cnblogs.com/    QQ,MSN:iamnewsea@hotmail.com

      如无特别标记说明,均为NewSea原创,版权私有,翻载必纠。欢迎交流,转载,但要在页面明显位置给出原文连接。谢谢。
  • 相关阅读:
    ABC065D Built[最小生成树]
    loj2718 「NOI2018」归程[Kruskal重构树+最短路]
    BZOJ1821 部落划分[最小生成树]
    BZOJ4777 [Usaco2017 Open]Switch Grass[最小生成树+权值线段树套平衡树]
    CF888G Xor-MST[最小生成树+01trie]
    Atcoder CODE FESTIVAL 2016 Final G
    BZOJ4883 [Lydsy1705月赛]棋盘上的守卫[最小基环树森林]
    BZOJ3714 [PA2014]Kuglarz[最小生成树]
    BZOJ1601 [Usaco2008 Oct]灌水[最小生成树]
    CF892E Envy[最小生成树]
  • 原文地址:https://www.cnblogs.com/newsea/p/3045437.html
Copyright © 2020-2023  润新知