• Redis编码问题


     

           最近搞redis存储对象出了点问题,大概说一下背景,项目原有的东东以前存的是redis,存储的直接是对象模型,没有问题,这里存储对象存储任何信息事都没有问题的。但是现在调整为存储序列化的json字符串,此时获取对象信息发生了问题,不是报错就是有乱码似的东东,一开始以为是编码问题,其实不准确,现在来一步步看一看到底是什么问题(这里的测试只是为了简单,命名等都不规范,大家凑活着看了解问题就行)

        

    public class test
    
        {
    
            public string Name { get; set; }
    
            public string View { get; set; }
    
     
    
            public IList<AAA> list { get; set; }
    
        }
    
     
    
        public class AAA
    
        {
    
            public string Name { get; set; }
    
            public string View { get; set; }
    
        }
    
     
    
        public class CartController : Controller
    
        {
    
            public void Index2()
    
            {
    
                test tes = new test();
    
                tes.Name = "z中文";
    
                tes.View = null;
    
                tes.list = new List<AAA>();
    
                AAA d = new AAA();
    
                d.Name = "123";
    
                d.View = "asd";
    
                AAA b = new AAA();
    
                b.Name = "我是特殊符号~!^?*$#<>\";
    
                b.View = "我是单引号"";
    
                tes.list.Add(d);
    
                tes.list.Add(b);
    
                //var aa = Newtonsoft.Json.JsonConvert.SerializeObject(tes);
    
                RedisManager.Execute(redis => redis.Set("test", tes));
    
                var ggg = RedisManager.Execute(redis => redis.Get<test>("test"));
    
            }
    
        }
    

     

     直接存储对象是没有问题的,看看其中的set和get吧,

        

    public bool Set<T>(string key, T value)
    
        {
    
          byte[] numArray = (object) value as byte[];
    
          if (numArray != null)
    
          {
    
            base.Set(key, numArray);
    
            return true;
    
          }
    
          else
    
          {
    
            string str = JsonSerializer.SerializeToString<T>(value);
    
            this.SetEntry(key, str);
    
            return true;
    
          }
    
    }
    
     
    
        public static string SerializeToString<T>(T value)
    
        {
    
          if ((object) value == null)
    
            return (string) null;
    
          if (typeof (T) == typeof (object) || typeof (T).IsAbstract || typeof (T).IsInterface)
    
          {
    
            if (typeof (T).IsAbstract || typeof (T).IsInterface)
    
              JsState.IsWritingDynamic = true;
    
            string str = JsonSerializer.SerializeToString((object) value, value.GetType());
    
            if (typeof (T).IsAbstract || typeof (T).IsInterface)
    
              JsState.IsWritingDynamic = false;
    
            return str;
    
          }
    
          else
    
          {
    
            StringBuilder sb = new StringBuilder();
    
            using (StringWriter stringWriter = new StringWriter(sb, (IFormatProvider) CultureInfo.InvariantCulture))
    
            {
    
              if (typeof (T) == typeof (string))
    
                JsonUtils.WriteString((TextWriter) stringWriter, (object) value as string);
    
              else
    
                JsonWriter<T>.WriteObject((TextWriter) stringWriter, (object) value);
    
            }
    
            return ((object) sb).ToString();
    
          }
    
        }
    
     
    
        public void SetEntry(string key, string value)
    
        {
    
          byte[] numArray = value != null ? ServiceStack.Text.StringExtensions.ToUtf8Bytes(value) : (byte[]) null;
    
          this.Set<byte[]>(key, numArray);
    
    }  

    恩恩,看样子应该是,存储的时候序列化了,并且使用utf8编码,那好吧,get肯定也就是utf8编码反序列化成对象取出来的,所以泛型的存取数据并没有跟编码有什么关系,那问题出在哪里呢。

        

    public T Get<T>(string key)
    
        {
    
          if (!(typeof (T) == typeof (byte[])))
    
            return JsonSerializer.DeserializeFromString<T>(this.GetValue(key));
    
          else
    
            return (T) base.Get(key);
    
        }
    
    public byte[] Get(string key)
    
        {
    
          return this.GetBytes(key);
    
        }
    
     
    
    public byte[] GetBytes(string key)
    
        {
    
          if (key == null)
    
            throw new ArgumentNullException("key");
    
          return this.SendExpectData(Commands.Get, StringExtensions.ToUtf8Bytes(key));
    
        }

    上面代码解释了为什么,对象怎么存储都没有问题,再来看看string类型的信息。

    public void Index2()
    
            {
    
                test tes = new test();
    
                tes.Name = "z中文";
    
                tes.View = null;
    
                tes.list = new List<AAA>();
    
                AAA d = new AAA();
    
                d.Name = "123";
    
                d.View = "asd";
    
                AAA b = new AAA();
    
                //b.Name = "我是特殊符号~!^?*$#<>\";
    
                b.Name = "2";
    
                //b.View = """;
    
                b.View = "\";
    
                tes.list.Add(d);
    
                tes.list.Add(b);
    
     
    
                var aa = Newtonsoft.Json.JsonConvert.SerializeObject(tes);
    
                RedisManager.Execute(redis => redis.Set("niutaotao_cart", aa));           
    
                var ggg = RedisManager.Execute(redis => redis.Get<byte[]>("niutaotao_cart"));
    
                var ii = RedisManager.Execute(redis => redis.Get<string>("niutaotao_cart"));
    
     
    
                var json="{"Name":"z中文","View":null,"list":[{"Name":"123","View":"asd"},{"Name":"2","View":"\"}]}";
    
                RedisManager.Execute(redis => redis.Set("ddd", json));
    
                var o = RedisManager.Execute(redis => redis.Get<byte[]>("ddd"));
    
                var pp = RedisManager.Execute(redis => redis.Get<string>("ddd"));
    
            }
    

    可以看看监视的结果,不多说直接上图。大概能看出点区别了。

      

     问题来了,1.json编码后首先转移符并没有特殊处理,而是直接写进了json格式字符串中

        2. 重现向上看取数据的时候 1.双引号有问题 ,貌似都变为了转移符+双引号

                     2.转移符有问题,具体规律也看不大出来,貌似就是之前都加了两个转移符

                     3。中文编码也有问题(这尼玛很奇怪啊,从上面代码来看,应该跟编码没关系才对)

    我们继续看代码,看看set存储数据的时候有什么特别的地方。我们可以看到对象和字符串的处理是不同的,嗯,估计问题就应该在这里了,看代码。

    {"Name":"z中文","View":null,"list":[{"Name":"123","View":"asd"},{"Name":"2","View":"\"}]}
    
     
    
    public static void WriteString(TextWriter writer, string value)
    
        {
    
          if (value == null)
    
            writer.Write("null");
    
          else if (!JsonUtils.HasAnyEscapeChars(value))
    
          {
    
            writer.Write('"');
    
            writer.Write(value);
    
            writer.Write('"');
    
          }
    
          else
    
          {
    
            char[] chArray = new char[4];
    
            writer.Write('"');
    
            int length = value.Length;
    
            for (int index = 0; index < length; ++index)
    
            {
    
              switch (value[index])
    
              {
    
                case '':
    
                  writer.Write("\b");
    
                  break;
    
                case '	':
    
                  writer.Write("\t");
    
                  break;
    
                case '
    ':
    
                  writer.Write("\n");
    
                  break;
    
                case 'f':
    
                  writer.Write("\f");
    
                  break;
    
                case '
    ':
    
                  writer.Write("\r");
    
                  break;
    
                case '"':
    
                case '\':
    
                  writer.Write('\');
    
                  writer.Write(value[index]);
    
                  break;
    
                default:
    
                  if ((int) value[index] >= 32 && (int) value[index] <= 126)
    
                  {
    
                    writer.Write(value[index]);
    
                    break;
    
                  }
    
                  else if ((int) value[index] < 55296 || (int) value[index] > 57343)
    
                  {
    
                    JsonUtils.IntToHex((int) value[index], chArray);
    
                    writer.Write("\u");
    
                    writer.Write(chArray);
    
                    break;
    
                  }
    
                  else
    
                    break;
    
              }
    
            }
    
            writer.Write('"');
    
          }
    
        }
    

      

    好了基本上找到原因了,问题就出在方法中列出的”\,””等特殊符号的问题,而且看((int) value[index] < 55296 || (int) value[index] > 57343)句话应该是对这个范围之外的符号进行了转码,具体什么转码方式小弟不清楚,

    所以呢,解决办法就来了,我们是不是可以在存储之前将这些被视为特殊符号,特殊处理的字符进行处理呢,然后区出来之后再解码是不是就可以了。

    好了试一把。我们就用UrlEncode试一下吧

    System.Web.HttpUtility.UrlEncode( ““, Encoding.UTF8);
    
    System.Web.HttpUtility. UrlDecode ( ““, Encoding.UTF8);
    
     
    
    var aa = Newtonsoft.Json.JsonConvert.SerializeObject(tes);
    
                var jj = System.Web.HttpUtility.UrlEncode(aa, Encoding.UTF8);
    
                RedisManager.Execute(redis => redis.Set("niutaotao_cart", jj));           
    
                var ggg = RedisManager.Execute(redis => redis.Get<byte[]>("niutaotao_cart"));
    
                var ii = RedisManager.Execute(redis => redis.Get<string>("niutaotao_cart"));
    
                var zz = System.Web.HttpUtility.UrlDecode(ii,Encoding.UTF8);
    

      

    看看结果

     

     

    呵呵,尼玛可以了。这里解决问题就可以了,关于redis的存储结构和实现,可以学习下下面两篇文章。

    关于redis的存储结构大家可以看看(http://www.cnblogs.com/shanyou/archive/2012/09/04/2670972.html

    关于redis的实现可以看看(http://www.searchtb.com/2011/05/redis-storage.html

    redis官网(http://www.redis.cn/

  • 相关阅读:
    [置顶] Spring的自动装配
    BZOJ2831(小强的金字塔系列问题--区域整点数求法)
    国际跆拳道联盟
    Linux如何查找某个时间点后生成的空文件
    ORACLE中关于外键缺少索引的探讨和总结
    ORA-12514, TNS:listener does not currently know of service requested in connect descriptor案例2
    SQL Server 2005 sp_send_dbmail出现Internal error at FormatRowset (Reason: Not enough storage is available to complete this operation)
    SQL Server数据库邮件发送异常案例
    MySQL二进制日志总结
    OSWatcher使用过程中小问题解决方法
  • 原文地址:https://www.cnblogs.com/superCow/p/3818538.html
Copyright © 2020-2023  润新知