Redis編碼問題

 

       最近搞redis存儲對象出了點問題,大概說一下背景,項目原有的東東之前存的是redis,存儲的直接是對象模型,沒有問題,這裏存儲對象存儲任何信息事都沒有問題的。可是如今調整爲存儲序列化的json字符串,此時獲取對象信息發生了問題,不是報錯就是有亂碼似的東東,一開始覺得是編碼問題,其實不許確,如今來一步步看一看究竟是什麼問題(這裏的測試只是爲了簡單,命名等都不規範,你們湊活着看了解問題就行)html

    

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吧,redis

    

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編碼反序列化成對象取出來的,因此泛型的存取數據並無跟編碼有什麼關係,那問題出在哪裏呢。json

    

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類型的信息。ide

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.雙引號有問題 ,貌似都變爲了轉移符+雙引號ui

                 2.轉移符有問題,具體規律也看不大出來,貌似就是以前都加了兩個轉移符this

                 3。中文編碼也有問題(這尼瑪很奇怪啊,從上面代碼來看,應該跟編碼不要緊纔對)編碼

咱們繼續看代碼,看看set存儲數據的時候有什麼特別的地方。咱們能夠看到對象和字符串的處理是不一樣的,嗯,估計問題就應該在這裏了,看代碼。3d

{"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/

相關文章
相關標籤/搜索