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 '\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编码问题的更多相关文章
- 基于Redis实现分布式锁
分布式锁具有的特性: 1.排他性: 文件系统: 数据库:主键 唯一约束 for update 性能较差,容易出现单点故障 锁没有失效时间,容易死锁 缓存Redis:setnx 实现复杂: 存在死锁(或 ...
- php redis 操作大全
类和方法 用法 Redis类 类RedisException 预定义的常量 Redis类 说明:创建一个Redis客户端 例 $redis = new Redis(); 类RedisException ...
- python - scrapy 爬虫框架 ( redis去重 )
1. 使用内置,并加以修改 ( 自定义 redis 存储的 keys ) settings 配置 # ############### scrapy redis连接 ################# ...
- Redis 数据结构之字符串的那些骚操作
Redis 字符串底层用的是 sds 结构,该结构同 c 语言的字符串相比,其优点是可以节省内存分配的次数,还可以... 这样写是不是读起来很无聊?这些都是别人咀嚼过后,经过一轮两轮三轮的再次咀嚼,吐 ...
- Golang 实现 Redis(11): RDB 文件解析
RDB 文件使用二进制方式存储 Redis 内存中的数据,具有体积小.加载快的优点.本文主要介绍 RDB 文件的结构和编码方式,并借此探讨二进制编解码和文件处理方式,希望对您有所帮助. 本文基于 RD ...
- scrapy-redis使用以及剖析
scrapy-redis是一个基于redis的scrapy组件,通过它可以快速实现简单分布式爬虫程序,该组件本质上提供了三大功能: scheduler - 调度器 dupefilter - URL去重 ...
- 爬虫基础(五)-----scrapy框架简介
---------------------------------------------------摆脱穷人思维 <五> :拓展自己的视野,适当做一些眼前''无用''的事情,防止进入只关 ...
- Scrapy-redis 组件
scrapy-redis 简介 scrapy-redis是scrapy框架基于redis数据库的组件,用于scrapy项目的分布式开发和部署. 特征 分布式爬取 可以启动多个spider工程,相互之间 ...
- 解读Scrapy框架
Scrapy框架基础:Twsited Scrapy内部基于事件循环的机制实现爬虫的并发.原来: url_list = ['http://www.baidu.com','http://www.baidu ...
随机推荐
- [POJ1088] 滑雪(递归dp)
Description Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激.可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你.Michael想知道 ...
- [Bnuz OJ]1176 小秋与正方形
传送门 问题描述 某天,acm的小秋拿到了一张很大很大的纸.他现在打算把它撕成正方 形.但是他没有任何工具,没有尺子,所以他尝试一种有趣的方法切分矩形.假设这是一个a*b的矩形(a>b),那么小 ...
- ASP渲染下拉框使时间依次减少
<% x=year(now()) y=year(now())-1 Do While y>2002%><li><a href="#201 ...
- css秘密花园
picture元素 http://www.w3cplus.com/responsive/responsive-images-101-part-6-picture-element.htmlCHAPTER ...
- 算法模板——sap网络最大流 3(递归+邻接矩阵)
实现功能:同之前 可以看见的是这次的程序优美了许多,代码简短了一倍还多,可是速度却是和原来的邻接表一个级别的(在Codevs上面草地排水那题的运行时间比较,但是显然数据很大时应该比那个慢些),原理差不 ...
- ajax(省,市,县)三级联动
下面我们用Jquery,ajax,做一个省,市,县的三级联动: 下面是我做三级联动下拉的步骤以及逻辑 第一步:先做一个省市区表格 第二步:建个PHP页面显示用我是在<body>里放< ...
- Eclipse添加tomcat出现 The Apache Tomcat installation at this directory is version 8.5.6. A Tomcat 8.0 installation is expected.
打开tomcat安装目录:apache-tomcat-8.5.6\lib 找到catalina.jar 用解压缩工具打开 org/apache/catalina/util/ServerInfo.pro ...
- hibernate从数据库中自动生成
计应134(实验班) 李佳鸿 DB Brower配置 1.依次选择window-Open Perspective-MyEclipse Explorer
- [设计模式] Iterator - 迭代器模式:由一份奥利奥早餐联想到的设计模式
Iterator - 迭代器模式 目录 前言 回顾 UML 类图 代码分析 抽象的 UML 类图 思考 前言 这是一包奥利奥(数组),里面藏了很多块奥利奥饼干(数组中的元素),我将它们放在一个碟子上慢 ...
- 有关rip路由协议相关知识以及实例配置【第1部分】
有关rip路由协议相关知识以及实例配置[第一部分] RIP呢,这是一个比较重要的知识点,所以它的知识覆盖面很广泛:但是呢,我将会对碰到的问题进行一些分析解刨(主要是为了帮助自己理清思维):也希望能够从 ...