最近搞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 '\b':

              writer.Write("\\b");

              break;

            case '\t':

              writer.Write("\\t");

              break;

            case '\n':

              writer.Write("\\n");

              break;

            case '\f':

              writer.Write("\\f");

              break;

            case '\r':

              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/

Redis编码问题的更多相关文章

  1. 基于Redis实现分布式锁

    分布式锁具有的特性: 1.排他性: 文件系统: 数据库:主键 唯一约束 for update 性能较差,容易出现单点故障 锁没有失效时间,容易死锁 缓存Redis:setnx 实现复杂: 存在死锁(或 ...

  2. php redis 操作大全

    类和方法 用法 Redis类 类RedisException 预定义的常量 Redis类 说明:创建一个Redis客户端 例 $redis = new Redis(); 类RedisException ...

  3. python - scrapy 爬虫框架 ( redis去重 )

    1.  使用内置,并加以修改 ( 自定义 redis 存储的 keys ) settings 配置 # ############### scrapy redis连接 ################# ...

  4. Redis 数据结构之字符串的那些骚操作

    Redis 字符串底层用的是 sds 结构,该结构同 c 语言的字符串相比,其优点是可以节省内存分配的次数,还可以... 这样写是不是读起来很无聊?这些都是别人咀嚼过后,经过一轮两轮三轮的再次咀嚼,吐 ...

  5. Golang 实现 Redis(11): RDB 文件解析

    RDB 文件使用二进制方式存储 Redis 内存中的数据,具有体积小.加载快的优点.本文主要介绍 RDB 文件的结构和编码方式,并借此探讨二进制编解码和文件处理方式,希望对您有所帮助. 本文基于 RD ...

  6. scrapy-redis使用以及剖析

    scrapy-redis是一个基于redis的scrapy组件,通过它可以快速实现简单分布式爬虫程序,该组件本质上提供了三大功能: scheduler - 调度器 dupefilter - URL去重 ...

  7. 爬虫基础(五)-----scrapy框架简介

    ---------------------------------------------------摆脱穷人思维 <五> :拓展自己的视野,适当做一些眼前''无用''的事情,防止进入只关 ...

  8. Scrapy-redis 组件

    scrapy-redis 简介 scrapy-redis是scrapy框架基于redis数据库的组件,用于scrapy项目的分布式开发和部署. 特征 分布式爬取 可以启动多个spider工程,相互之间 ...

  9. 解读Scrapy框架

    Scrapy框架基础:Twsited Scrapy内部基于事件循环的机制实现爬虫的并发.原来: url_list = ['http://www.baidu.com','http://www.baidu ...

随机推荐

  1. (@WhiteTaken)UGUI中遇到的一些细碎的知识点

    最近接触Unity中UGUI的知识比较多,遇到的东西,就慢慢积累下来吧.用到就不用去网上找了. 1.Unity加载Sprite图片资源.在Unity中,我们可能会遇到,一张图片中,有多个UI,这时候导 ...

  2. 转换器3:手写PHP转Python编译器,词法部分

    上周写了<ThinkPhp模板转Flask.Django模板> 一时技痒,自然而然地想搞个大家伙,把整个PHP程序转成Python.不比模板,可以用正则匹配偷懒,这次非写一个Php编译器不 ...

  3. Repcached实现memcached复制

    1.介绍     repcached是日本人开发的实现memcached复制功能,它是一个单 master单 slave的方案,但它的master/slave都是可读写的,而且可以相互同步,如果 ma ...

  4. 1651: [Usaco2006 Feb]Stall Reservations 专用牛棚

    1651: [Usaco2006 Feb]Stall Reservations 专用牛棚 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 566  Sol ...

  5. java自加和自减

    public class Add { public static void main(String[] args) { int i = 0; i=i++ + ++i; int j = 0; j= ++ ...

  6. pycharm社区版无database 解决方法

    第一步,点击file/setting/plugins 如下图所示 第二步,搜索database 安装database Nivagator 并Apply 第三步,新建数据库连接,open sql con ...

  7. Android开发之AsyncTask示例Demo

    今天做了一个AsyncTask的小Demo,内含注释,通过此Demo,可以对AsyncTask有一个详细的了解 已经将项目上传到了GitHub上(程序有一个小bug,在第一次提交有说明,有解决方法请留 ...

  8. PHP流程管理,堪比小小程序

    这个流程管理是从用户登录界面开始,然后提交申请,页面逐级审核通过.这个做起来其实挺简单,只是在某些逻辑方面需要 好好考虑一下. 登录页面就不再多说了,如果要存session的话,我们可以建一个假的登录 ...

  9. ThinkPHP3.2.3版本验证码异步第二次验证时失败的问题解决

    最近在用TP3.2.3做一个小项目,纠结于验证码验证问题,重点在于二次验证,举个例子就是常见的登录页面上有个验证码输入框,当用户输入验证码并且鼠标点击在这个输入框之外时候,触发onblur事件,然后a ...

  10. 【Java基础 】Java7 NIO Files,Path 操作文件

    从Java1.0到1.3,我们在开发需要I/O支持的应用时,要面临以下问题: 没有数据缓冲区或通道的概念,开发人员要编程处理很多底层细节 I/O操作会被阻塞,扩展能力有限 所支持的字符集编码有限,需要 ...