解决对象序列化与反序列化失败且第一个字符为”问号”难题

后端 潘老师 2周前 (04-07) 25 ℃ (0) 扫码查看

最近遇到对象在序列化与反序列化过程中出现异常。下面就来详细说说这个“对象转成json后转成byte[]后再转成string,反序列化时提示失败且第一个字符是问号”的问题,以及该如何解决。

一、问题复现

在实际开发场景中,我们需要将一个对象先转成json格式,再把json转成byte[] ,通过网络传输后,再将其反序列化为对象。然而,在反序列化这一步却出现了错误。当打印出要反序列化的json字符串时,发现其开头多了一个问号,这就是导致反序列化失败的“罪魁祸首”。

为了让大家更清楚,下面看看具体的代码示例。

(一)相关类定义

  1. TextMessage类
using System.Threading;

namespace SocketTools
{
    public class TextMessage : Message
    {

        private string message;

        public string Message { get => message; set => message = value; }

        public TextMessage()
        {

        }


        public TextMessage(string userName, string targetName, string sendTime, string message) : base(userName, targetName, sendTime, MessageType.Text)
        {
            this.message = message;
        }
    }
}

这个类继承自Message类,用于表示文本消息,包含消息内容等属性。

  1. Message类
using Newtonsoft.Json;

namespace SocketTools
{

    public enum MessageType
    {
        Connection = 0,
        Text,
        Image,
        Mixed,
        Recall,
        File,

    }

    public class Message
    {
        private string userName;
        private string targetName;
        private string sendTime;
        private MessageType messageType;

        public string UserName { get => userName; set => userName = value; }
        public string TargetName { get => targetName; set => targetName = value; }
        public string SendTime { get => sendTime; set => sendTime = value; }
        public MessageType MessageType { get => messageType; set => messageType = value; }

        public Message()
        {

        }

        public Message(string userName, string targetName, string sendTime, MessageType messageType)
        {
            UserName = userName;
            TargetName = targetName;
            SendTime = sendTime;
            MessageType = messageType;
        }

       // 序列化方法
       public static string Serialize(Message message)
       {
           return JsonConvert.SerializeObject(message);
       }

       // 反序列化方法
       public static T Deserialize<T>(string json) where T : Message, new()
       { 
   return JsonConvert.DeserializeObject<T>(json );
  
       }
     

    }

}

Message类是一个基础类,定义了消息的一些通用属性和序列化、反序列化方法,其中MessageType是一个枚举类型,用于表示消息的类型。

(二)执行代码

using SocketTools;
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

namespace FunctionalTesting
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            string msg = "消息内容Q1_2_end";
            string userName = "AAAAAAAAAAAAAAAA";
            string targetUserName = "CCCCCCCCCCCCCCCC";


            TextMessage textMessage = new TextMessage(userName,
                targetUserName,
                DateTime.Now.ToString("yy-MM-dd-H:m:s"),
                msg);

            // 按照指定的格式创建消息字符串
            string message = Message.Serialize(textMessage);
            Console.WriteLine(message);
            // 将消息字符串转换为字节流
            byte[] data;
            using (MemoryStream ms = new MemoryStream())
            {
                using (StreamWriter sw = new StreamWriter(ms, Encoding.UTF8))
                {
                    sw.Write(message);
                    sw.Flush();
                    ms.Position = 0;
                    data = ms.ToArray();
                }
            }
            string data2 = Encoding.UTF8.GetString(data);
            Console.WriteLine(data2);
            data2 = Regex.Replace(data2, @"^\s+", "");
            Console.WriteLine(data2);

            var d = Message.Deserialize<TextMessage>(data2);
            Console.WriteLine(d.TargetName);
            Console.WriteLine(d.UserName);
            Console.WriteLine(d.MessageType);
            Console.ReadLine();

        }
    }
}

在这段代码中,我们创建了一个TextMessage对象,将其序列化后转换为字节流,再转换回字符串,最后尝试反序列化。

(三)执行效果与错误日志

执行上述代码后,输出的json字符串开头出现了问号:

?{"Message":"消息内容Q1_2_end","UserName":"AAAAAAAAAAAAAAAA","TargetName":"CCCCCCCCCCCCCCCC","SendTime":"23-12-04-1:14:43","MessageType":1}

同时,反序列化时抛出错误,错误日志如下:

Newtonsoft.Json.JsonReaderException
  HResult=0x80131500
  Message=Unexpected character encountered while parsing value: . Path '', line 0, position 0.

但打印原本未经过转换的对象序列化后的字符串,是没有开头这个问号的。

二、解决方式

针对这个问题,有两种常见的解决办法。

(一)去除前导字符

修改反序列化代码,直接去除字符串开头的前导字符。可以使用TrimStart方法,代码如下:

// 反序列化方法
public static T Deserialize<T>(string json) where T : Message, new()
{ 
    return JsonConvert.DeserializeObject<T>(json.TrimStart('\uFEFF'));
}

这里的\uFEFF就是导致问题的特殊字符,通过TrimStart方法将其去除后,反序列化就能正常进行。

(二)条件判断后去除

还可以通过判断字符串的第一个字符来决定是否去除。如果第一个字符不是{ 或者[ ,就去除第一个字符,代码如下:

public static T Deserialize<T>(string json) where T : Message, new()
{ 
    if (json[0] == '{' || json[0] == '[')
    {
    }
    else
    {
        json = json.Remove(0, 1);
    }
    return JsonConvert.DeserializeObject<T>(json);
}

这种方式更加灵活,能适应更多复杂的情况。

三、问题原因

出现这个问题的根源在于零宽度不中断空格(Unicode U+FEFF)字符,也就是常说的BOM(Byte Order Mark)。在使用Unicode编码的文本文件或数据流中,BOM字符用来表示字节顺序。比如在UTF – 16编码里,一个字符由两个字节组成,不同系统和平台下这两个字节的顺序可能不同,BOM字符就能帮助解析器确定字节顺序,正确解码文件中的其他字符。

但在网络通信、数据库存储等数据交换场景中,BOM字符可能会被意外包含在字符串里,这就会导致解析错误。所以在处理这类字符串前,检查并移除不需要的BOM字符是很有必要的。

通过以上对问题的复现、解决方式的介绍以及原因剖析,希望大家在遇到类似问题时,能够快速定位并解决。


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/back/16834.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】